zoukankan      html  css  js  c++  java
  • 简单工厂模式

    这次我们将通过对代码一点点的改进,逐步了解简单工厂模式

    业务需求:数据库操作(增加学生,删除员工,查询老师,更新老师)。

    第一步:面向过程的编程,main方法中实现所有功能。最符合人编程思维的方式。

    1 public static void main(String[] args) {
    2     System.out.println("增加学生成功");
    3     System.out.println("删除员工成功");
    4     System.out.println("查询老师成功");
    5     System.out.println("更新老师成功");
    6 }

    UML图:

    这段代码完成功能,但是所有功能都在main中实现了,不利于复用(下次有相同的业务的时候,只有复制粘贴过去)并不符合单一职责原则。

    第二步:封装一下刚刚的代码吧,吧main中的方法提取出来封装起来。

     1 class Util1 {
     2   void addStudent() { System.out.println("增加学生成功"); }
     3   void deleteEmployee() { System.out.println("删除员工成功"); }
     4   void queryTeacher() { System.out.println("查询老师成功"); }
     5   void updateTeacher() { System.out.println("更新老师成功"); }
     6 }
     7 
     8 public static void main(String[] args) {
     9   Util1 util1 = new Util1();
    10   util1.addStudent();
    11   util1.deleteEmployee();
    12   util1.queryTeacher();
    13   util1.updateTeacher();
    14 }

    UML图(关联长得和书上好像有点不一样。。)

    这样看起来代码是可以复用的,但是仔细想想老师的查询和学生的增加放在一起怪怪的。

    有可能我们修改了学生的增加方法,却连老师的查询方法一起重新编译了,大大增加了编译的耗时,这是我们不愿意看到的。

    项目的重新编译是一个耗时的工作,在分秒必争的行业中显然是不被允许。

    第三步:数据库操作代码分类

     1 public static void main(String[] args) {
     2     StudentService studentService=new StudentService();
     3     EmployeeService employeeService=new EmployeeService();
     4     TeacherService teacherService=new TeacherService();
     5 
     6     System.out.println(studentService.myAddStudent());
     7     System.out.println(employeeService.myDeleteEmployee());
     8     System.out.println(teacherService.myQueryTeacher());
     9     System.out.println(teacherService.myUpdateTeacher());
    10 }
    11 class StudentService{
    12     public String myAddStudent() {
    13         return "增加学生成功";
    14     }
    15     public String myDeleteStudent() {
    16         return "删除学生成功";
    17     }
    18     public String myQueryStudent() {
    19         return "查询学生成功";
    20     }
    21     public String myUpdateStudent() {
    22         return "更新学生成功";
    23     }
    24 }
    25 class EmployeeService {
    26     public String myAddEmployee() {
    27         return "增加员工成功";
    28     }
    29     public String myDeleteEmployee() {
    30         return "删除员工成功";
    31     }
    32     public String myQueryEmployee() {
    33         return "查询员工成功";
    34     }
    35     public String myUpdateEmployee() {
    36         return "更新员工成功";
    37     }
    38 }
    39 class TeacherService {
    40     public String myAddTeacher() {
    41         return "增加老师成功";
    42     }
    43     public String myDeleteTeacher() {
    44         return "删除老师成功";
    45     }
    46     public String myQueryTeacher() {
    47         return "查询老师成功";
    48     }
    49     public String myUpdateTeacher() {
    50         return "更新老师成功";
    51     }
    52 }

    UML图:

    补充:因为是对数据库的操作,这里把service改成dao会比较好

    大体到这里就比较像我们平时项目中的代码,service中管理dao层的操作。
    但是仔细分析一下还是会发现,耦合度太高了(因为使用的是关联,是一种强关联)。
    而且通过观察,我们可以发现其实每个对数据的操作大体都是增删改查,所以我们可以先抽象一个关于dao层操作的接口。

    第四步:数据库操作代码封装接口

     1  public static void main(String[] args) {
     2         BaseDao employeeDao=new EmployeeDao();
     3         BaseDao studentDao=new StudentDao();
     4         BaseDao teacherDao=new TeacherDao();
     5 
     6         System.out.println(employeeDao.myAdd());
     7         System.out.println(employeeDao.myQuery());
     8         System.out.println(studentDao.myDelete());
     9         System.out.println(teacherDao.myUpdate());
    10     }
    11 interface BaseDao {
    12     String myAdd();
    13     String myDelete();
    14     String myQuery();
    15     String myUpdate();
    16 }
    17 
    18 class StudentDao implements BaseDao {
    19     @Override
    20     public String myAdd() {
    21         return "增加学生成功";
    22     }
    23     @Override
    24     public String myDelete() {
    25         return "删除学生成功";
    26     }
    27     @Override
    28     public String myQuery() {
    29         return "查询学生成功";
    30     }
    31     @Override
    32     public String myUpdate() {
    33         return "更新学生成功";
    34     }
    35 }
    36 
    37 class EmployeeDao implements BaseDao {
    38     @Override
    39     public String myAdd() {
    40         return "增加员工成功";
    41     }
    42     @Override
    43     public String myDelete() {
    44         return "删除员工成功";
    45     }
    46     @Override
    47     public String myQuery() {
    48         return "查询员工成功";
    49     }
    50     @Override
    51     public String myUpdate() {
    52         return "更新员工成功";
    53     }
    54 }
    55 
    56 class TeacherDao implements BaseDao {
    57     @Override
    58     public String myAdd() {
    59         return "增加老师成功";
    60     }
    61     @Override
    62     public String myDelete() {
    63         return "删除老师成功";
    64     }
    65     @Override
    66     public String myQuery() {
    67         return "查询老师成功";
    68     }
    69     @Override
    70     public String myUpdate() {
    71         return "更新老师成功";
    72     }
    73 }

     UML图:

    到这里直观的可以看到service中不再需要关联所有用到的数据库操作类,只需要关联也数据库操作相关的接口就可以了。
    看着自己的SSM的项目中,dao层mybatis那块都用的是接口,controller调用的也是service的接口,虽然不明白其中接口的好处,但是好像已经在很多地方使用到了接口。

    面向接口编程的好处
    在传统的项目开发过程中,由于客户的需求经常变化,如果不采用面向接口编程,那么我们必须不停改写现有的业务代码。

    改写代码可能产生新的BUG,而且改写代码还会影响到调用该业务的类,可能全都需要修改,影响系统本身的稳定性。

    而且为了将改写代码带来的影响最小,我们不得不屈服当前的系统状况来完成设计,代码质量和稳定性更低。

    当这种情况积累到一定程度时,系统就会出现不可预计的错误,代码凌乱,不易读懂,后接手的人无法读懂代码,系统的维护工作越来越重,最终可能导致项目失败。

    接口在项目就是一个业务逻辑,面向接口编程就是先把客户的业务提取出来,作为接口。

    业务具体实现通过该接口的实现类来完成。

    当客户需求变化时,只需编写该业务逻辑的新的实现类,修改实现类,不需要改写现有代码,减少对系统的影响。(遵循开闭原则)

    接口是对业务抽象。我们制定规则,具体的实现不用关心。
    讲讲我对接口编程的好处的理解:
    还是我们刚刚那个demo,现在增加一个查询所有在校学生的接口,同时查询所有学生的接口也要保留。

    如果我们直接修改原来的代码会导致查询所有学生的接口报错。但是如果我们新建一个类,方法,重新制定实现类。

    业务倒是完成但是在维护的时候人可读性怎么样呢?系统的稳定性又如何?
    (补充一下接口隔离原则。我们刚刚例子中的接口看起来已经很简洁了,虽然只修改查询方法,但是要把删除,增加,更新重新实现了一遍,所以再把代码改改。接口的功能过于复杂)

     1 public static void main(String[] args) {
     2     //现在是查询所有学生
     3     StudentDaoV1 studentDaoV1=new StudentDaoV1();
     4     studentDaoV1.queryStudent();
     5     //修改成查询在校学生
     6     StudentDaoV2 studentDaoV2=new StudentDaoV2();
     7     studentDaoV2.queryStudent();
     8 }
     9 class StudentDaoV1
    10 {
    11     public void queryStudent()
    12     {
    13         System.out.println("查询所有学生");
    14     }
    15 }
    16 class StudentDaoV2
    17 {
    18     public void queryStudent()
    19     {
    20         System.out.println("查询在校学生");
    21     }
    22 }

    如果我们用接口的方式来实现一下

     1 public static void main(String[] args) {
     2     //现在是查询所有学生
     3     QueryInterface queryInterface = new StudentQueryV1();
     4     queryInterface.Query();
     5     //修改成查询在校学生
     6     queryInterface = new StudentQueryV2();
     7     queryInterface.Query();
     8 }
     9 interface QueryInterface {
    10     void Query();
    11 }
    12 
    13 class StudentQueryV1 implements QueryInterface {
    14     @Override
    15     public void Query() {
    16         System.out.println("查询所有学生");
    17     }
    18 }
    19 
    20 class StudentQueryV2 implements QueryInterface {
    21     @Override
    22     public void Query() {
    23         System.out.println("查询在校学生");
    24     }
    25 }

    使用接口编程的话,我们只需要替换一下实现类就可以了。而且通过接口我们可以很肯定实现类的功能是查询学生,通过v2可以判断是第2版的查询学生。
    优点:
    1.不影响其他类的功能
    2.代码可读性强
    3.只需替换实现类
    但是如果我们实例化错了对象怎么办?因为全局需要更改的实例化的对象很多。
    我们可以使用简单工厂帮助我们来实例化对象

    第五步:使用简单工厂模式

     1 public static void main(String[] args) {
     2     //目标增加学生,删除员工,查询老师,更新老师
     3     MySimpleFactory mySimpleFactory = new MySimpleFactory();
     4     System.out.println(mySimpleFactory.getMyService(0).myAdd());
     5     System.out.println(mySimpleFactory.getMyService(1).myDelete());
     6     System.out.println(mySimpleFactory.getMyService(2).myQuery());
     7     System.out.println(mySimpleFactory.getMyService(3).myUpdate());
     8 }
     9 interface BaseService {
    10     String myAdd();
    11     String myDelete();
    12     String myQuery();
    13     String myUpdate();
    14 }
    15 
    16 class StudentService implements BaseService {
    17     @Override
    18     public String myAdd() {
    19         return "增加学生成功";
    20     }
    21     @Override
    22     public String myDelete() {
    23         return "删除学生成功";
    24     }
    25     @Override
    26     public String myQuery() {
    27         return "查询学生成功";
    28     }
    29     @Override
    30     public String myUpdate() {
    31         return "更新学生成功";
    32     }
    33 }
    34 
    35 class EmployeeService implements BaseService {
    36     @Override
    37     public String myAdd() {
    38         return "增加员工成功";
    39     }
    40     @Override
    41     public String myDelete() {
    42         return "删除员工成功";
    43     }
    44     @Override
    45     public String myQuery() {
    46         return "查询员工成功";
    47     }
    48     @Override
    49     public String myUpdate() {
    50         return "更新员工成功";
    51     }
    52 }
    53 class TeacherService implements BaseService {
    54     @Override
    55     public String myAdd() {
    56         return "增加老师成功";
    57     }
    58     @Override
    59     public String myDelete() {
    60         return "删除老师成功";
    61     }
    62     @Override
    63     public String myQuery() {
    64         return "查询老师成功";
    65     }
    66     @Override
    67     public String myUpdate() {
    68         return "更新老师成功";
    69     }
    70 }
    71 class MySimpleFactory {
    72     BaseService getMyService(int code) {
    73         BaseService baseService;
    74         //0代表StudentService
    75         //1代表EmployeeService
    76         //其余代表TeacherService
    77         switch (code) {
    78             case 0:
    79                 baseService = new StudentService();
    80                 break;
    81             case 1:
    82                 baseService = new EmployeeService();
    83                 break;
    84             default:
    85                 baseService = new TeacherService();
    86         }
    87         return baseService;
    88     }
    89 }

    UML图:

     其实这么一步步的走过来可以发现,代码其实越来越多(哈哈哈哈)。

    但是每一步都是有意义的

    第一次改进:将代码抽象出来,减少代码冗余,增加复用,使得修改抽象代码可以修改全局代码。

    第二次改进:将业务逻辑分类。该处理学生的业务在一起,该处理老师的业务放在一起...软件出了问题,需要及时定位,修改,编译,发布。

    分离业务以后,我们能快速定位到问题,修改,编译,发布。

    第三次改进:我们将相同行为抽象成接口。首先我们在一开始就为业务设计了接口。修改业务也不需要改动接口,增加新的实现方法。

    这样的话,首先维护和修改的时候可读性高,同时只需要替换实现类,对于不需要修改的代码没有什么影响。

    第四次改进:我们使用了简单工厂模式:通过我们传入的参数,工厂生产返回我们想要的实现类。我们只需要对操作类进行操作。

    假设我们现在使用了查询所有学生的接口,现在需要替换成查询在校学生的接口。那么我们需要全局替换。

    使用简单工厂模式,我们只需修改工厂的返回对象(修改一次)。

    优点:对象的创建和使用分离,遵循单一职责原则
    缺点:增加新的产品对象时必须修改工厂方法,违背开闭原则

    使用场景:
    模式简单,一般用于小项目或者很少扩展的情况
    不适用于多层次的树状结构

    总的来说,现在将代码复杂化,是为了将来修改维护起来更加容易。

    会有不需要修改的和维护的软件吗?

    没有,还不快好好学习设计模式

    啰啰嗦嗦的写了很多,其实简单工厂的讲解还是挺少的,主要是为了在这个过程中好好感受面向对象的思想。

    再啰嗦一点复习下面向对象:

    一切事物皆为对象(特定的属性和行为)

    类就是将一些具有相同属性和行为的对象的抽象集合。

    方法的重载是为了不影响原方法的基础上,新增功能

    三大特性:

    封装:将一组对象抽象成类

    继承:继承是一种is-a的关系 猫是一种动物 猫继承动物。可以降低重复代码,代码得到共享,不易出错。

    多态:不同的对象执行相同的动作,通过继承抽象类来实现相同的动作。

    抽象类:提供继承的出发点,可以多个抽象类继承。尽可能多的共同代码和尽可能少的数据。建议是树枝节点都是抽象类,叶子节点都是实现类。

    接口:公共方法和属性(针对不同的类),封装特定功能的集合。

  • 相关阅读:
    .NET.GC 浅谈.net托管程序中的资源释放问题 (转帖)
    VB 中实现 For Each
    创建 ODBC 数据源以连接到 Windows CE 设备上的Sybase数据库
    VB 调用水晶报表2
    VC数据类型
    对.Net 垃圾回收Finalize 和Dispose的理解
    C# 播放器
    [转载]帮助C#菜鸟进入GDI+开发
    线程间操作无效: 从不是创建控件 的线程访问它
    VB.Net 中 WithEvents、AddHandler
  • 原文地址:https://www.cnblogs.com/Gang-Bryant/p/10767655.html
Copyright © 2011-2022 走看看