工厂模式

写在前面

在面向对象编程(OOP)中,通过new去实例化一个对象是再正常不过的事情。但是每当new一个对象,后面都会跟一个具体类,这使得代码与具体类过度耦合,让我们陷入面向实现编程的漩涡里面。每当有新的需求变更,我们代码的扩展性就比较差。

手动new对象有哪些问题

  • 与具体类耦合,每当有新的变化,就必须改变代码(不符合对修改关闭)
  • 无法借助接口、多态的力量去隔离变化(与面向接口编程相违背)

如何才能优雅的new对象

  • 对扩展开放,应对后期的各种需求
  • 对修改关闭,对于新的需求,不改变原有的代码
  • 不关心构造对象的细节和复杂过程,轻松获取对象实例

可以通过工厂模式来构造我们想要的对象。

工厂模式是一种常用的创建型设计模式,在基类中定义了创建对象的接口,让子类来决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。

工厂模式包括我们所熟知的简单工厂模式、工厂方法模式、抽象工厂工厂模式。其中简单工厂模式在一定程度上只算是一种编程思想,还谈不上设计模式。但它作为工厂方法模式和抽象工厂模式的基石,还是有必要深入了解一下。

造车案例来了解一下不同的工厂模式

  1. 客户想要一辆奔驰车,客户需要奔驰车的设计图纸相应的汽车零件来自己制造。当客户想要悍马车,手上的设计图纸和汽车零件已经毫无用处,需要获取悍马车的设计图纸以及汽车零件(不符合对修改关闭)
  2. 客户不需要自己生产汽车,由工厂来提供服务,客户需要什么车,工厂就生产什么车。但是每当客户有新的需求的时候,工厂就得想尽办法去找相关的图纸和零件来交付生产。---简单工厂模式(不符合对修改关闭)
  3. 客户的需求太多,一个工厂完全应付不过来,所以工厂就开始建立新的场地,有些场地只生产宝马车,有些场地只生产悍马车,这样客户想要什么样的车就找对应的车工厂去提货。--- 工厂方法模式
  4. 客户的需求逐渐升级,不同型号的车具有不同配置,所以每一个具体工厂应该具备生产不同型号车辆的能力,奥迪的工厂不仅能生产A4,也能生产A6、A8。所以一个工厂应该具备生产不同型号车辆的生产车间---抽象工厂模式

几种工厂模式的实现

简单工厂

定义一个工厂类,根据传入的参数不同返回不同的实例。

public interface Shape {
    void draw();
}
public class RectShape implements Shape {
    @Override
    public void draw() {
        System.out.println("draw rectangle");
    }
}
public class CircleShape implements Shape {
    @Override
    public void draw() {
        System.out.println("draw circle");
    }
}
public class ShapeFactory {
    public static Shape getShape(String type){
        Shape shape = null;
        if("circle".equalsIgnoreCase(type)){
            shape = new CircleShape();
        }else if("rectangle".equalsIgnoreCase(type)){
            shape = new RectShape();
        }
        return shape;
    }

    public static void main(String[] args) {
        Shape circle = ShapeFactory.getShape("circle");
        circle.draw();

        Shape rectangle = ShapeFactory.getShape("rectangle");
        rectangle.draw();
    }
}
  • 应用场景:
    • 只有一个工厂类,对于创建对象不多的案例比较适合
    • 工厂类封装了对象的创建过程,客户端不需要关心对象的创建过程。

工厂方法

简单工厂的深入化, 通过创建不同的对象工厂来取代统一的工厂。让子类来决定哪一个类实例化,让一个类的实例化延迟到子类。

案例:有一个图片加载器,可以加载jpg、png、gif等图片格式,

/**
 * 图片加载器接口
 */
public interface Reader {
    void read();
}
/**
 *
 * gif 图片加载器
 */
public class GifReader implements Reader{
    @Override
    public void read() {
        System.out.println("gif picture reader");
    }
}
/**
 *
 * png 图片加载器
 */
public class PngReader implements Reader{
    @Override
    public void read() {
        System.out.println("png picture reader");
    }
}
/**
 *
 * jpg 图片加载器
 */
public class JpgReader implements Reader{
    @Override
    public void read() {
        System.out.println("jpg picture reader");
    }
}
/**
 * gif 图片加载器工厂
 */
public class GifReaderFactory implements ReaderFactory{
    @Override
    public Reader getReader() {
        return new GifReader();
    }
}
/**
 * jpg 图片加载器工厂
 */
public class JpgReaderFactory implements ReaderFactory{
    @Override
    public Reader getReader() {
        return new JpgReader();
    }
}
/**
 * png 图片加载器工厂
 */
public class PngReaderFactory implements ReaderFactory {
    @Override
    public Reader getReader() {
        return new PngReader();
    }
}
/**
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        GifReaderFactory gifReaderFactory = new GifReaderFactory();
        Reader gifReader = gifReaderFactory.getReader();
        gifReader.read();

        PngReaderFactory pngReaderFactory = new PngReaderFactory();
        Reader pngReader = pngReaderFactory.getReader();
        pngReader.read();

        JpgReaderFactory jpgReaderFactory = new JpgReaderFactory();
        Reader jpgReader = jpgReaderFactory.getReader();
        jpgReader.read();

    }
}

工厂方法模式优点

  • 良好的封装性。调用者需要创建一个产品对象,只需要知道产品工厂的类名就可以了,不要了解对象创建过程,降低模块间的耦合。
  • 良好的扩展性。当有新增产品类的需求变化,只要适当扩展一个工厂类就可以完成拥抱变化
  • 屏蔽产品类。产品类如何变化,调用者不需要关心---切换数据源(数据库从MySQL切换到Oracle,需要改动的只是切换一下驱动名称)
  • 典型的解耦框架。
    • 高层模块需要知道产品抽象类,不关心实现类---迪米特法则
    • 只依赖产品类的抽象 --- 依赖倒置原则
    • 产品子类替换产品父类 --- 里氏替换原则

应用场景

  • 所以生成对象的地方都可以使用工厂方法模式,但是要权衡增加工厂类进行管理带来的代码复杂度
  • 需要灵活、可扩展的框架是,可以采用工厂方法模式
  • 客户端不需要知道它所创建对象的类,只需要知道创建的工厂名就可以完成创建过程

抽象工厂

为创建一组相关或相互依赖的对象提供一个借口,而无需指定他们的具体类。

案例:设计一个兼容Android、ios、Wp三个操作系统的游戏,每个系统都有一套操作控制和界面控制器

/**
 * 界面控制器
 */
public interface UIController {
    void display();
}
/**
 * 操作控制器
 */
public interface OperationController {
    void control();
}
/**
 * wp ui controller
 */
public class WpUIController implements UIController {
    @Override
    public void display() {
        System.out.println("wp ui controller");
    }
}
/**
 * ios 界面控制器
 */
public class IosUIController implements UIController {
    @Override
    public void display() {
        System.out.println("ios ui controller");
    }
}
/**
 * 安卓界面控制器
 */
public class AndroidUIController implements UIController{
    @Override
    public void display() {
        System.out.println("android ui controller");
    }
}
/**
 * wp 操作控制器
 */
public class WpOperationController implements OperationController {
    @Override
    public void control() {
        System.out.println("wp operation controller");
    }
}
/**
 * ios 操作控制器
 */
public class IosOperationController implements OperationController {
    @Override
    public void control() {
        System.out.println("ios operation controller");
    }
}
/**
 * 安卓操作控制器
 */
public class AndroidOperationController implements OperationController{
    @Override
    public void control() {
        System.out.println("android opration controller");
    }
}
public interface SystemFactory {
    OperationController createOperationController();
    UIController createUIController();
}
/**
 * ios 工厂
 */
public class IosFactory implements SystemFactory {
    @Override
    public OperationController createOperationController() {
        return new IosOperationController();
    }

    @Override
    public UIController createUIController() {
        return new IosUIController();
    }
}
/**
 * wp 工厂
 */
public class WpFactory implements SystemFactory{
    @Override
    public OperationController createOperationController() {
        return new WpOperationController();
    }

    @Override
    public UIController createUIController() {
        return new WpUIController();
    }
}
/**
 * 安卓工厂
 */
public class AndroidFactory implements SystemFactory {
    @Override
    public OperationController createOperationController() {
        return new AndroidOperationController();
    }

    @Override
    public UIController createUIController() {
        return new AndroidUIController();
    }
}
public class Client {
    public static void main(String[] args) {
        AndroidFactory androidFactory = new AndroidFactory();
        OperationController androidOperation = androidFactory.createOperationController();
        androidOperation.control();

        UIController androidUI = androidFactory.createUIController();
        androidUI.display();

        IosFactory iosFactory = new IosFactory();
        OperationController iosOperation = iosFactory.createOperationController();
        iosOperation.control();

        UIController iosUI = iosFactory.createUIController();
        iosUI.display();

        WpFactory wpFactory = new WpFactory();
        OperationController wpOperation = wpFactory.createOperationController();
        wpOperation.control();

        UIController wpUI = wpFactory.createUIController();
        wpUI.display();
    }
}

优点

  • 封装性。高层模块不需要关心产品实现类

缺点

  • 产品族扩展困难,当要新增一个控制器,所有的实现类都要改变---违反开闭原则,改变了契约,所有与契约有关系的代码都要改变
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容