zoukankan      html  css  js  c++  java
  • 迈向架构设计师之路系列—简单对象访问模式(一)

      假设场景

      现在假如公司要你做一个公司内部的薪资管理系统,根据职位的不同,每月的工资自然不一样,经理一月10000加上分红1000,技术人员一月5000加上200的餐补,客服一月3000,现在要是由你来做,你会怎么设计?代码无错便是优已经不适用了

      阅读目录

      一:大部分人的写法v1.0

      二:第一次改版后的代码v1.1

      三:第二次改版后的代码v1.2

      四:第三次改版后的代码v1.3

      五:UML类图解析

      六:总结

      七:思考

      一:大部分人的写法v1.0

      这样的写法会带来一个问题?什么问题呢?复用性的问题

      假如现在你接了个私活,别的公司让你也写个公司内部的薪资计算系统,你说那还不简单,把代码复制过去就行啊,有人说过初级程序员的工作就是Ctrl+C和Ctrl+V,这其实是非常不好的编码习惯,因为当你的代码中重复的代码多到一定程度的时候,维护起来就是一场灾难,越大的系统这种方式带来的问题越严重,编程有一个原则就是尽可能的想办法避免重复,再说了比如客户要求你给它们公司做的内部薪资计算系统是个Web版的,或者是个WindowsApplication版的,那么你下面的代码就废掉了,想想看,薪资计算系统哪些是和控制台程序无关的,只和计算有关的,也就是说分出一个类让计算和显示分开,也就是要让业务和界面分开

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;

      namespace _1_SimpleFactory
      {
          class Program
          {
              static void Main(string[] args)
              {
                    try
                    {
                        int intSalary = 0; ;//薪水
                        string strPost = Console.ReadLine();//职位
                        switch (strPost)
                        { 
                            case "经理":
                                intSalary = 10000 + 1000;
                               break;
                            case "技术":
                                intSalary = 5000 + 200;
                               break;
                            case "客服":
                                intSalary = 3000;
                               break;
                        }
                        Console.WriteLine("工资是:" + intSalary);
                        Console.ReadLine();
                     }
                      catch (Exception ex)
                     { 
                     }
              }
          }
      }

      

       二:第一次改版后的代码v1.1

      为什么要改版?因为考虑到复用性的问题

      要让业务逻辑和界面逻辑分开,让它们的耦合度下降,只有分离开才能达到更容易维护和扩展,也就是业务的封装

      我们新建一个类库,名字叫BusinessLogic,里面有个类文件叫Calculate.cs,专门处理根据职位计算工资的,当我们需要做个Web版的话,只需要把界面逻辑文件Program.cs代码中的“Console.WriteLine("工资是:" + intSalary);Console.ReadLine();”换成“ASP.NET的Response.Write(("工资是:" + intSalary);Response.End();”就OK了,业务逻辑文件Calculate.cs是可以复用的,这样就达到了业务和界面分离了,如果你现在要做一个Windows版的,手机版的,PDA版的,都可以复用这个Calculate类

      这次改版后的代码修复了原始代码不能复用性的问题,这里只是用到了面向对象三个特性中的封装特性,把业务逻辑封装起来,继承和多态在这里还没用到呢

      这样的写法又会带来一个新的问题?缺乏灵活性,灵活性包含两个方面,一个方面是修改性,一个方面则是扩展性

      已经把业务和界面分离了不是很灵活了吗?好那我们现在打个比方,比方CEO现在要在公司中安排一些亲戚进来工作,职位给了个虚头衔是助理,亲戚自然不能亏待,工资是每月6000+1000,你该怎么设计呢?只要修改Calculate.cs 业务逻辑文件,在里面加个分支就行了,问题来了,如果你这样做,你只是要增加一个职位却让“经理”和“技术”以及“客服”参加了编译,这是糟糕的,你万一一不小心把case "经理":intSalary = 10000 + 1000;改成了case "经理":intSalary = 1000 + 1000;你们经理该弄死你了,其次你看到“经理”补助1000,我们技术部补助才200,他奶奶的,不行我要提高我们部门的待遇,然后你把case "技术":intSalary = 5000 + 200;改写成intSalary = 5000 + 800;这样你们部门每个技术都要比原来多发600元人民币了,本来是让你增加一个功能,却让原来运行良好的代码产生了变化,这是非常糟糕的且有巨大风险的,那么我们下面该怎么设计呢?

       1:Calculate.cs 业务逻辑文件

       using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;

      namespace BusinessLogic
      {
          public class Calculate
          {
              /// <summary>
              /// 计算工资
              /// </summary>
              /// <param name="strPost">职位描述</param>

          ///<returns>工资</returns>
              public static int CalculateSalary(string strPost)
              {
                  int intSalary = 0; ;//薪水
                  switch (strPost)
                  {
                      case "经理":
                          intSalary = 10000 + 1000;
                         break;
                      case "技术":
                          intSalary = 5000 + 200;
                         break;
                      case "客服":
                          intSalary = 3000;
                         break;
                  }
                  return intSalary;
              }
          }
      }

      2:Program.cs 界面逻辑文件

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using BusinessLogic;

      namespace _1_SimpleFactory
      {
          class Program
          {
              static void Main(string[] args)
              {
                  try
                  {
                      int intSalary = Calculate.CalculateSalary(Console.ReadLine());
                      Console.WriteLine("工资是:" + intSalary);
                      Console.ReadLine();
                  }
                  catch (Exception ex)
                  { 
                    
                  }
              }
          }
      }

      工程架构图

      

      三:第二次改版后的代码v1.2

      为什么要再次改版呢?因为考虑到灵活性的问题

      我们要让“经理”和“技术”以及“客服”运算类分开,修改其中一个类不影响其他的类,增加“助理”等运算类也不影响其他的代码

      首先还是在名字叫BusinessLogic的类库里面有个类文件叫Calculate.cs的计算工资类,里面有个虚方法CalculateSalary(),用于计算工资的,然后我们把“经理”和“技术”及“客服”和“助理”写成了计算工资类的子类,继承后,重写了CalculateSalary()方法,这样要修改任何一个职位的工资,就不需要提供其他职位的代码了,其次要增加一个职位,只需要新建一个类继承计算工资类,重写CalculateSalary()方法就行了,也不用提供其他职位的代码了

      这次改版后的代码修复了第一版代码不具有灵活性的问题

      这样写又会带来一个新的问题?什么问题呢?就是如何让客户端知道我想用哪个计算工资类呢?我们是用CalculateJinLi.cs这个经理计算工资类,还是用CalculateJiShu.cs这个技术计算工资类呢?

      1:Calculate.cs

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;

      namespace BusinessLogic
      {
          /// <summary>
          /// 计算工资抽象类,这个抽象不是指把它定义为抽象类,而是它存在的意义是为了抽象
          /// </summary>
          public class Calculate
          {
              /// <summary>
              /// 计算工资
              /// </summary>
              ///<returns>工资</returns>
              public virtual int CalculateSalary()
              {
                  int intSalary = 0; ;//薪水
                  return intSalary;
              }
          }
      }

      2:CalculateJinLi.cs

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;

      namespace BusinessLogic
      {
          /// <summary>
          /// 计算经理工资的类,继承计算工资类
          /// </summary>
          public class CalculateJinLi:Calculate
          {
              public override int CalculateSalary()
              {
                  int intSalary = 10000 + 1000 ;//薪水
                  return intSalary;
              }
          }
      }

      3:CalculateJiShu.cs

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;

      namespace BusinessLogic
      {  
          /// <summary>
          /// 计算技术工资的类,继承计算工资类
          /// </summary>
          public class CalculateJiShu:Calculate
          {
              public override int CalculateSalary()
              {
                  int intSalary = 5000 + 200;
                  return intSalary;
              }
          }
      }

      4:CalculateKeFu.cs

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;

      namespace BusinessLogic
      {
          /// <summary>
          /// 计算客服工资的类,继承计算工资类
          /// </summary>
          public class CalculateKeFu:Calculate
          {
              public override int CalculateSalary()
              {
                  int intSalary = 3000;
                  return intSalary;
              }
          }
      }

      5:CalculateZhuLi.cs

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;

      namespace BusinessLogic
      {
          /// <summary>
          /// 计算助理工资的类,继承计算工资类
          /// </summary>
          public class CalculateZhuLi:Calculate
          {
              public override int CalculateSalary()
              {
                  int intSalary = 6000 + 1000;
                  return intSalary;
              }
          }
      }

      工程架构图

      

       四:第三次改版后的代码v1.3

       第二次代码改版后遇到的问题就是如何去实例化对象的问题,到底要实例化谁,将来会不会增加实例化的对象,比如增加“助理”职位的计算工资,这是很容易变化的地方,以后保不准会增加“销售”这个职位的计算工资类等,应该考虑一个单独的类来做这个创造实例的过程,这就是工厂

      在第二版代码上,我们增加CalculateFactory这个类库,里面有个CalculateFactory.cs类文件,类文件中有个CreateCalculate()方法,根据输入的职位,工厂实例化出合适的对象,通过多态返回父类的方式实现计算工资的结果

      1:CalculateFactory.cs 工厂类文件

      using System.Linq;
      using System.Text;
      using BusinessLogic;

      namespace CalculateFactory
      {  
          /// <summary>
          /// 计算工资工厂类
          /// </summary>
          public class CalculateFactory
          {
              /// <summary>
              /// 根据传入的职位创造对应的实例
              /// </summary>
              /// <param name="strPost">职位</param>
              /// <returns>Calculate抽象类</returns>
              public static Calculate CreateCalculate(string strPost)
              {
                  Calculate calulate = null;
                  switch (strPost)
                  { 
                      case "经理":
                          calulate = new CalculateJinLi();
                          break;
                      case "技术":
                          calulate = new CalculateJiShu();
                          break;
                      case "客服":
                          calulate = new CalculateKeFu();
                          break;
                      case "助理":
                          calulate = new CalculateZhuLi();
                          break;
                  }
                  return calulate;
              }
          }
      }

      2:Program.cs 客户端文件

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using BusinessLogic;
      using CalculateFactory;

      namespace _1_SimpleFactory
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Calculate calculate = null;
                  //由工厂根据参数决定要实例化哪个对象
                  calculate = CalculateFactory.CalculateFactory.CreateCalculate("经理");
                  int intResult = calculate.CalculateSalary();
                  Console.WriteLine("经理工资为:" + intResult);
                  calculate = CalculateFactory.CalculateFactory.CreateCalculate("技术");
                  intResult = calculate.CalculateSalary();
                  Console.WriteLine("技术工资为:" + intResult);
                  calculate = CalculateFactory.CalculateFactory.CreateCalculate("客服");
                  intResult = calculate.CalculateSalary();
                  Console.WriteLine("客服工资为:" + intResult);
                  calculate = CalculateFactory.CalculateFactory.CreateCalculate("助理");
                  intResult = calculate.CalculateSalary();
                  Console.WriteLine("助理工资为:" + intResult);
                  Console.ReadLine();
              }
          }
      }

      运行效果图

      

      工程架构图

      

       五:UML类图解析

      统一建模语言UML(是Unified Modeling Language)的缩写,是用来对软件密集系统进行可视化建模的一种语言,UML为面向对象开发系统的产品进行说明,可视化,和编制文档的一种标准语言

      类图分为三层,第一层显示类的名称,如果是抽象类,则用斜体表示,第二层是类的特性,通常是字段和属性,第三层是类的操作,通常是方法和行为,前面的符号“+”,表示public,”-“表示private,“#”表示protected

      

      六:总结

      这样我们就完成了一个具有复用性,灵活性(可修改,可扩展)的公司内部薪资管理系统,我们同时也看到这样一个小小的公司内部薪资系统,都用到了封装,继承,多态

      七:思考

      如果我们有一天我们需要更改经理的工资怎么办?我们只需要改CalculateJinLi.cs里的代码就行了,如果我们需要增加“销售”职位的工资算法怎么办?我们只需要增加相应的子类就行了,还要去计算工资工厂在switch里增加分支就行了,那我们要去修改界面怎么办?比如我们换成Web版的,那就去改界面呀,跟运算毫无关系

      这个只是24种设计模式中最简单的“简单对象访问模式”,其他很多设计模式有的很复杂,由于平时工作很忙,没有及时更新请大家谅解,请关注我的博客

    记录,成为更好的自己
  • 相关阅读:
    HttpClient POST/GET方法
    Selenium+Java(十一)Selenium窗口切换
    Selenium+Java(十)Selenium常用方法
    Selenium+Java(九)Selenium键盘与鼠标事件
    Selenium+Java(八)Selenium下拉框处理
    Selenium+Java(七)Selenium对话框的处理
    Selenium+Java(六)Selenium 强制等待、显式等待、隐实等待
    [java]对象创建的过程
    [正则表达式] 表达式使用记录
    【Mysql】主从复制
  • 原文地址:https://www.cnblogs.com/menglin2010/p/2343020.html
Copyright © 2011-2022 走看看