工厂模式—Head First 设计模式

本文通过讲解Head First 设计模式中的工厂模式,探讨如何动态创建对象。文章以披萨订购为例,介绍了简单工厂、工厂方法和抽象工厂模式,阐述了它们的实现方式和优缺点,强调了代码的可扩展性和遵循依赖倒置原则的重要性。
摘要由CSDN通过智能技术生成

今天这个模式是时时刻刻围绕着怎么动态创建对象这个问题来的!!

在Java中,创建对象必须要用new来实现,通过Java的多态性,我们可以设计很多子类实现同一个父类,这样便可以使用语言多态性来使对象具有动态运行时类型,这样可以一定程度上给对象创建提供一定的灵活性。

还是以书上的例子进行讲解:

就拿顾客预定Pizza来说,Pizza的类型有很多种,有培根Pizza,香肠Pizza......,这样在我们的代码中应该怎样创建对象呢?不难想到:
          可以根据用户需求来创建,于是有了下面的代码:(我们将这个过程称为orderPizza)

Pizza orderPizza(String type){

if(type.equals("chess"))

      Pizza = new chessPizza();

else if(type.equals("greek"))

    Pizza = new greekPizza();

else 

   return null;

}

来设想以后的一种场景:当Pizza的类型增多的时候,我们需要在原有的代码中增加Pizza类型匹配的代码,这样一段代码就会被重新打开,很显然,这不符合我们的设计原则:代码应该对扩展开放,对修改关闭。

让我们按照从简单到复杂的过程来思考怎么解决这个问题:

结合之前接触的将代码中变化的部分和不变的部分分开的原则,我们尝试将代码中这段”动态增长“的代码从他原来的地方提取出来,单独放到一个类中进行Pizza创建时的类型选择,这样一个被单独提出来用来确定Pizza类型的类,就像一个加工的工厂,我们把这种简单的希望对对象创建实现封装的类叫做简单工厂类(SimplePizzaFactory)

上面的方法并没有使用什么特殊的模式,而仅仅是将需要改变的代码提取出来放在一个单独的类中,我们将上面这种方法叫做简单工厂方法。

来想想简单工厂怎么实现从用户点餐到最终满足用户的过程来尝试绘制一下简单工厂方法的类图吧!

首先根据用户点的匹萨的不同。我们需要返回给用户不同的披萨,用户需求提供产品,这个过程就像是工厂加工商品一样,不如把这个根据用户的需求返回特定对象的类叫做简单披萨工厂类,得到了这个满足用户需求的披萨后,我们还有一系列的制作过程,比如烘焙。切片,装盒...他们操作的对象都是Pizza,所以Pizza对象中自然有这些方法了。为了接受用户的请求,我们还需要一个PizzaStore类,来获得用户的请求并将请求传递到简单披萨工厂来得到一个特定披萨,调用Pizza对象的烘焙,切片,装盒等方法来完成对Pizza对象的处理。

其类图如下:

 

这样,我们新加一种类型的Pizza中不必打开原始的类增增改改,而只需要在提取并新建的类中增加分支语句,就可以使它提供更多种类的Pizza了。

设想一下现在新增Pizza类型的情形,只需要让新增加的Pizza类继承Pizza父类,并在SimplePizzaFactory中增加一个新的分支语句就可以了。

对代码的上述修改并不能称为一个模式,但是在编程中注意到这种变动并保持这样的代码编写规范无疑是能对我们后期维护带来好处的!

紧接着,来设想另一种情况,对于上述披萨,不同地区的同一种披萨也是有很大的不同的。

如果比萨这种现象不明显的话,你可以联想一下生活实际,如果你生活在北方,在你没来南方之前你会认为世界上的番茄炒蛋都是咸的,豆花也都是咸的,哪有那么傻的人吃甜的番茄炒蛋和豆花.....而此刻的南方人可能也在心里暗暗想:应该不会有人傻到吃咸的番茄炒蛋和豆花吧。

这就是所谓的地区差异,怎么解决这个问题呢?

这不是很简单嘛!在比如有纽约风味的奶酪披萨和芝加哥风味的奶酪披萨,就分别涉及两个类并继承Pizza这个父类呗?这样用户说明他需要一个芝加哥风味的奶酪披萨就给他一个芝加哥风味的奶酪,需要什么风味就给他什么风味嘛!

理论上这当然说得过去,但是想想这样的设计会导致什么.......首先你的SimplePizzaFactory中的if-else将会变的非常长,假设有n种类型的Pizza,有m种风味,那无疑你的分支将会达到n*m中!!!再想想实际:芝加哥地区的人是经常吃芝加哥风味的披萨还是会经常吃一个纽约风味的披萨?当然是吃本地风味的多一些啊!!之所以会出现不同风味的披萨就是为了适应不同地区人的口味(当然,这里所说的是统计学中的概率,你不能因为因为投掷10次硬币10次花朝上就说下次投掷一定是花朝上)。

现在问题变成了,怎么做能让我的SimplePizzaFactory中的对披萨类型的比较只变成我们经常使用的那些而又能很好的满足客户需求呢?

我们来尝试创建不同风味的工厂:

      这样我们在PizzaStore中对PizzaStore动态选择特定类型的PizzaFactory就可以了。

     进一步,我们发现似乎有些问题,甚至还会带来一些问题,假如用户需要一个纽约风味的Pizza:NYCheesePizza,还要从中拆解出NY和Cheese这两个关键字,这不是自找麻烦吗!

于是我们改进之后的类图如下所示:

         这样虽然共同继承Pizza父类的子类 并没有分队排列,但是由于特定的比萨店只出售特定类型的Pizza,从而达到了一种类似分类划分的景象(当你在代码中将PizzaStore实例化为一个纽约风味的披萨店,他就默认你选择的Pizza是纽约风味的,具体什么种类根据用户需求而定)。

         以上设计模式,我们将其称作工厂方法模式

       接下来给出工厂方法模式的定义:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法把类的实例化推迟到子类。


       还有一个和工厂方法模式很像的模式,叫抽象工厂模式,它是指,如果我们披萨中的每一种原料不再仅仅用字符串来标志,而是将他们也作为一个个对象,比如披萨中所用的面团,原来我们用String来表示他,在代码中用String dough(面团),现在,我们用Dough nydough;来表示。

按照如下类图进行设计就是使用了抽象工厂:


抽象工厂模式:提供一个接口用于创建相关或依赖对象的家族,而不需要明确指出具体类。 

 

今天学到的模式的工厂模式优点:

1.所有的工厂都是用来封装对象的创建。

2.简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类中解耦。

3.工厂方法使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象。抽象工厂使用对象组合,对象的创建被实现在工厂接口所暴露出的方法中。

4.所有的工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。

5.工厂方法允许将实例化延迟到子类进行。

6.抽象工厂创建相关对象家族,而不需要依赖他们的具体类。

7.依赖倒置原则,指导我们避免依赖具体类型,而要尽量依赖抽象。

 

OO原则:
依赖抽象,而不依赖具体类。

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值