设计模式(一)工厂模式

来源:互联网 发布:php 入门 编辑:程序博客网 时间:2024/04/28 07:45

题记

   顾客:老板,给我来个对象。
   工厂:好嘞。

概述

常用的工厂模式有3种:简单工厂模式,工厂模式,抽象工厂模式。它们的作用从名字就可以看出:就是制造出对象返回给客户端(即需要对象的地方)。大多数时候我们需要对象时都是直接使用new来创建对象实例的,这样必然造成代码中各个类之前相互依赖(即变量持有具体类的引用),使得代码内部耦合度过高。反应出来的问题就是当某个地方需要修改时,就会修改一大片代码甚至包括客户端代码,这违背了一个重要软件设计原则:开闭原则(对扩展开放,对修改封闭)。下面看一个例子:在一个年终总结大会上,需要每个人都发言,回顾一下过去,展望一下未来,于是就需要很多不同类型的对象实例。通常,我们的做法是直接在客户端new一个实例对象,然后调用该对象的方法。如下:

类的关系
   上图中Person类为抽象类,2个子类实现抽象方法speak。
   Person代码:
public abstract class Person {    public abstract void speak();}
       2个人物代码:
public class Coder extends Person {    @Override    public void speak() {        System.out.println("老板,说好的程序员鼓励师呢?");    }}
public class Boss extends Person {    @Override    public void speak() {        System.out.println("只要大家好好工作,明年我就请程序员鼓励师!");    }}
   有了人物,接下来就可以开总结大会了,代码如下:
//总结大会public class Summary {    Person person;    //大会发言    private void summarySpeak(String type){        if("coder".equals(type)){            person = new Coder();        }else if("boss".equals(type)){            person = new Boss();        }        person.speak();    }}
    那么问题来了:HR表示我也要发言。这个时候就需再创建一个基类为Person类的HR子类(这里还只是扩展),然后修改Summary类中大会发言的判断代码加上HR的判断(修改了客户端代码),违背了开闭原则(可以想象只要有新角色加入,就得修改客户端代码)。这时候就该工厂模式闪亮登场了。

一、简单工厂模式

软件编程的一个重要原则是:封装改变,把变化的部分分离出去。既然问题在于发言角色会经常变化,为什么不单独创建一个
(简单工厂类)来专门生成我想要的角色呢?
  继续添加新角色:
Hr代码:
public class Hr extends Person {    @Override    public void speak() {        System.out.println("大家有话好好说,请不要跳槽!!!");    }}
SimpleFactory代码:
public class SimpleFactory {    //创建角色    public Person getPerson(String type){        if("coder".equals(type)){            return new Coder();        }else if("boss".equals(type)){            return new Boss();        }else if("hr".equals(type)){            return new Hr();        }        return null;    }
简单工厂

可以看出,Summary类里面持有2个引用:person和simpleFactory,其中person是通过simplFactory获取的。我们把创建角色的代码移到了简单工
厂类里面,Summary类里面只是持有简单工厂的引用,当需要创建角色时,直接调用该引用的getPerson方法即可。这样虽然以后有新角色加入,我们
还是得修改SimpleFactory这个类里面的代码,但是请注意,这个时候我们不再需要去修改客户端里面的代码了(即Summary类里面的代码),因为
Summary里面只是单存地调用SimpleFactory里面的getPerson方法获取角色实例。其实这里的修改已经可以理解为是一种扩展了。
以上就是简单工厂的使用,简单来说就是:把需要创建新对象的部分封装到一个单独的工厂类中(即new发生在简单工厂类中),客户端直接调用
工厂的创建对象方法获取实例。好处是把客户端代码解耦,减少对实例类的依赖,以后的修改也只是修改工厂类而已,不影响客户端代码。

二、工厂模式

工厂模式

从上图可以看出,工厂模式特点是:把创建对象的任务交给了创建者的子类,由子类角色到底要创建什么产品。父类可以是一个接口或者抽象类。
那么这样做有什么意义呢?他对扩展封闭对修改开放怎么体现的呢?在哪些地方做到解耦了呢?
其实可以这么想:既然创建对象的任务交给了子类,那么父类(此时即为抽象类)和客户端自然不再关心到底是什么具体类型(这就体现了解耦
),它们只需要调用抽象的getProduct方法获取对象(实际创建的是什么对象是子类的责任,我不关心),然后调用该对象的方法完成操作即可。
我们还是举上面的例子来说:



总结大会工厂模式类图

  PersonFactory代码:
public abstract class PersonFactory {    Person person;    //总结大会开始    public void summarySpeak(){        person = getPerson();        person.speak();    }    //创建角色    public abstract Person getPerson();}
BossFactory代码:(其他2个类似)
public class BossFactory extends PersonFactory {    @Override    public Person getPerson() {        return new Boss();    }}
可以看到父类中的得获取person的方法为抽象的,交给了子类来具体创建,父类只管开总结大会,即调用person的speak方法发言即可(因为开会
中每个人任务都是一样的:发言,所以这里把开会放到了父类中,即使以后需要每个人唱歌也很好扩展,直接加上person.sing()即可)。子类则负
责具体创建哪个角色,它们真正决定了每个人怎么发言。即使以后有新角色加入,或者Boss的发言改变了,也不需要修改父类中的代码。
以上就是工厂模式的应用,简单来说,工厂模式就是把创建对象的任务交给了子类执行(由继承实现),父类和客户端不需要关系到底是创建的
什么类型,它们只要调用获得的类型的方法完成自己所关心的任务即可(如上面的完成总结大会),这就是解耦的一种体现,也是一种针对抽象编程
的体现。

三、抽象工厂模式


抽象工厂模式

从上图可以看出:抽象工厂模式其实是对工厂模式的扩展,工厂模式是要创建一种类型的对象(如上面只是创建Person类的不同子类),而抽象工
厂模式是要创建多种类型的对象,然后把这些对象组合起来使用。如上图,创建对象的任务依然交给了子类来觉得,但是子类里面需要决定创建多种不
同具体的产品(如ProductA的1还是2,ProductB的1还是2)。
回到公司开总结大会的例子上来,显然只是发一个言是不能满足一个总结大会的要求的,大伙儿最期待的还是领取年终奖。所以加入领取年终奖的
环节。这个类图如下:


公司开年会

可以看到开年会doSummary被放到抽象类CommpanySummary里面,里面就是调用person.speak()和prize.getPrize()。而每个部门都需要开年会,
发言和发奖品也有差异,所以每个部门就是一个工厂,它们创建的是一堆对象,这些对象来决定抽象类CommpanySummary里面的doSummary怎么进行:
谁发言,怎么发奖品等。
抽象工厂可以看成是工厂模式的扩展,或者说抽象工厂里面包含了工厂模式。抽象工厂主要是创建一堆对象,把这些对象组合起来完成一个功
能。而每一个对象的创建又可以看出是工厂模式的体现。

四、结束语

整个工厂模式其实都是对变化的封装,面向抽象编程,把具体类型和客户端解耦,很好地体现了开闭原则。工厂模式的主要功能就是创建对象,
根据创建对象的方式,地点不同又可以分为:简单工厂模式,工厂模式,抽象工厂模式。

五、参考资料

1.《Head First设计模式》

0 0
原创粉丝点击