当前位置 : 主页 > 编程语言 > 其它开发 >

创建型模式之门—简单工厂

来源:互联网 收集:自由互联 发布时间:2022-05-30
1.“new”有什么不对劲? 在我们没有接触到工厂模式(简单工厂、工厂方法模式、抽象工厂模式)之前,我们实例化对象唯一的方法就是通过“new”关键字来完成。但是,大量的使用“
1.“new”有什么不对劲?

在我们没有接触到工厂模式(简单工厂、工厂方法模式、抽象工厂模式)之前,我们实例化对象唯一的方法就是通过“new”关键字来完成。但是,大量的使用“new”关键字来实例化对象会违背一些设计原则,因为代码与具体的类型绑在一起,从而导致过多的依赖于细节而非抽象,这样代码就很难适应需求变化。

在面向对象编程中我们大量的使用到了继承、多态的特性,但是在使用这些特性时往往会延申出一些新的问题。例如在一个有继承模块的代码当中,如果子类发生新增或删除,这就不得不去使用类的“调用层”做出相应的修改,因为你new的都是具体的类型,当下层发生变动就不得不去上层进行修改。对于这样的场景来说,实际上它也违背了设计模式原则之一的“开闭原则”,即对扩展开放,对修改关闭,这会不利于程序的稳定和扩展。

 

2.举例反映问题

接下来将通过代码案例来反映出,在一个使用继承模块的代码当中,大量使用“new”关键字,在设计层面会带来什么样的缺陷。我们以一个汽车销售系统作为背景,其中汽车类型UML图如下:

其中有一个卖车的方法使用到了“new”来实例化车型对象,相应的代码如下:

 1     //卖车方法    
 2     public void SellCar(string carType)
 3     {
 4         Car car;
 5         if(carType=="宝马 X3")
 6         {
 7             car=new Bmwx3();
 8         }
 9         else if(carType=="吉普牧马人") 
10         {
11             car=new Jeeper();
12         }
13         else if(carType=="奔驰G63") 
14         {
15             car=new BenzG63();
16         }
17         
18         TestDrive(car);//试驾
19         Collection(car);//收款
20         License(car);//上牌
21     }

在实际的生活当中我们应该都知道,4s店卖的车型不可能是一层不变的,而是经常性的会发生产品的更新换代,所以会不断对车型做出新增或删减。如果汽车销售系统中使用了以上的代码,这就意味这每次车型模块的调整都要同步到“卖车方法”中,并且随着公司规模的增大所卖的车型会增多,在新增车型后代码中出现大量的else if,这会显得非常臃肿,在删除时还可能误删。

即便你说你的“汽车销售系统只卖一种车”(也就是只new一种对象类型),你可能还是会在使用构造函数创建对象时要加入参数的情形,这些变化都会促使你因为下层模块的调整而影响到上层模块。

 

 3.改善方法

1.编程当中任何需求或代码都是无法避免的,而我们能做到就是运用设计模式的思想,能够将程序更好的去适应变化。其中的一个思想就是:“找出变化的部分,把它们从不变的部分分离出来”。以上面的“汽车销售系统”中的卖车方法为例,分析变化如下:

2.然后,我们将变化的部分单独的封装到一个类的静态方法中,该类的静态方法将专门用于创建具体的汽车对象,代码如下:

 1 public class CarFactory
 2 {
 3     public static Car CreateCarInstance(string type)
 4     {
 5         Car car=null;
 6         if(carType=="宝马 X3")
 7         {
 8             car=new Bmwx3();
 9         }
10         else if(carType=="吉普牧马人") 
11         {
12             car=new Jeeper();
13         }
14         else if(carType=="奔驰G63") 
15         {
16             car=new BenzG63();
17         }
18         return car;
19     }
20 }

3.在抽离并封装变化后,我们将对“卖车方法”中的实例化车对象部分进行改造。

1   public void SellCar(string carType)
2     {
3         Car car=CarFactory.CreateCarInstance(carType);
4                
5         TestDrive(car);//试驾
6         Collection(car);//收款
7         License(car);//上牌
8     }

根据以上的三个步骤,其实一个简单工厂创建对象的方式就已经形成了,其中专门创建对象的Factory类我们称之为工厂,它实例化的对象我们称之为产品。此时我们创建对象的方式不在是"new",而是通过一个静态方法。

对于上层模块(卖车方法),在也不用担心下层模块(汽车车型)的变化而牵连到自身,它只有根据指定的类型创建对应的车型对象即可,并且上层模块(卖车方法)也不用在承担对象创建的细节,减少大量代码和复杂度。

另外简单工厂也解决了对象创建复用的问题,例如4s店的汽车保险系统、汽车维修系统等等都会用到实例化车型对象。这样的话,如果车型发生修改我们只用调整简单工厂类即可而其他(调用层)都不必受到改动的牵连。

 

 4.定义

简单工厂又称之为静态工厂方法,它属于创建型模式中的一种简单运用。

严格来讲它其实并不属于一种设计模式,反而比较像我们在遵循设计模式原则时的一种编程习惯。你要你能够在编程中遵循并灵活运用:迪米特法则(最少知道原则)、依赖倒置(依赖抽象,不依赖细节)、里氏替换(子类替代父类)这些原则,其实就会无形当中使用简单工厂来实现对象的实例化,而并非只使用“new”。

 简单工厂的UML图:

5.短板

简单工厂虽然有一些优点:将对象创建这件事进行封装,促使上层隔离变化,实现了对扩展开放,对修改关闭,并且可以达到对象创建功能的复用。然而它还存在一定程度的不足之处:简单工厂中创建的对象类型种类繁多,这不符合单一职责原则。另外,上层模块和下层模块之间并没有通过抽象建立依赖,这意味着它不符合依赖倒置原则,如果要改变使用其他工厂创建对象,此时的简单工厂是不具备适应变化的能力。

还是以汽车销售作为背景来说,假如你依赖的这个A工厂因为疫情出现停产,又或者因为你要扩张你的车型使用新的工厂,那么此时你依赖的A工厂(具体的类)就很难做出扩展,而只能因为你换了工厂导致上层模块都要跟着调整。所以,后续诞生出的“工厂方法模式”和“抽象工厂模式”,就是在不断解决因为变化而带来各种的问题。

知识改变命运 【文章原创作者:韩国机房 http://www.558idc.com/kt.html欢迎留下您的宝贵建议】
网友评论