工厂模式

SimpleFactory

说到工厂,我们应该能想到,造汽车的有汽车工厂,造轮船的有轮船工厂,映射到Java中,造对象的就应该有对象工厂,比如说我想要买一辆车,那我只需要去汽车工厂(先不去4S店)里面告诉它什么品牌的汽车,具体参数,给钱提车就完了,我并不需要了解汽车是如何造出来的,也就是屏蔽了造车的细节,这样的好处就是对于买家来说,简单方便,用户体验大大提升,对于卖家来说我厂生产汽车的具体细节不会外露。

需求:

用户提出要圆形、三角形、菱形等等。

解决方案1:

public void m1() {
    // 用户需要圆,自己new一个
    Circle circle = new Circle();
    // 用户需要三角形,自己new一个
    Triangle triangle = new Triangle();
}

解决方案2:

可以使用简单工厂的模式,只需要将工厂暴露给调用方,调用方给出自己想要的形状就可以了,因为所有的设计模式都是面向接口编程,所以接口是少不了的。

定义一个Shape接口

/**
 * Description: 所有图形都有的公共属性,定义成抽象类也可以,接口也可
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/7
 * @Since v1.0
 */
public interface Shape {

    void draw();

}

Shape接口的实现类CricleShape

/**
 * Description: 圆形实例
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/7
 * @Since v1.0
 */
public class CircleShape implements Shape {

    public CircleShape() {
        System.out.println("圆形创建出来。");
    }

    @Override
    public void draw() {
        System.out.println("画出一个圆形。");
    }

}

Shape接口的实现类Triangle

/**
 * Description: 三角形实例
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/7
 * @Since v1.0
 */
public class TriangleShape implements Shape {

    public TriangleShape() {
        System.out.println("三角形创建出来。");
    }

    @Override
    public void draw() {
        System.out.println("画出一个三角形");
    }

}

简单工厂类,对外暴露方法

/**
 * Description: 简单工厂类
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/7
 * @Since v1.0
 */
public class ShapeFactory {

    /**
     * 1. 简单工厂模式对外屏蔽了对象的创建细节,需要什么对象直接从工厂获取。
     * 2. 将获取对象的方法声明成静态的,由工厂类,向外提供服务。
     */
    public static Shape getShape(String shapeName) {
        Shape shape = null;
        if (null != shapeName && !"".equals(shapeName)) {
            if ("circle".equalsIgnoreCase(shapeName)) {
                shape = new CircleShape();
            } else if ("triangle".equalsIgnoreCase(shapeName)) {
                shape = new TriangleShape();
            }
        }
        return shape;
    }

}

测试类

在调用方只需要给工厂提供想要的形状即可,不必关心形状是如何来的。

/**
 * Description: 简单工厂模式测试用例
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/7
 * @Since v1.0
 */
public class Test01 {

    @Test
    public void m1() {
        Shape circle = ShapeFactory.getShape("circle");
        circle.draw();
    }

}

简单工厂,根本来说不属于23中设计模式中的一种,但工厂方法模式和抽象工厂模式都是由它演变来的,它的缺点:系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,有可能造成工厂逻辑过于复杂,违背了OCP原则。

FactoryMethod

还是上面的汽车例子,简单工厂类中维护了创建不同汽车的逻辑,比如说包括宝马,奔驰,奥迪,但后续需求越来越多,又增添了长城,马自达,大众等等品牌,那么这个工厂类的创建逻辑会越来越多,现实中,宝马有宝马自己的工厂,奔驰也有奔驰自己的工厂,映射到Java中,不同的类,应该有各自的工厂,这样的目的就是为了维护扩展方便,同时遵循了OCP原则。

设计模式是基于接口的,首先要有一个汽车的接口Car,它用来规定汽车的公共功能:

/**
 * Description: 所有汽车的公共接口
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public interface Car {

    /**
     * car在跑。
     */
    void running();

}

然后是具体品牌汽车的实例:

宝马:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class BaoMa implements Car {

    @Override
    public void running() {
        System.out.println("宝马飞驰。");
    }

}

奔驰:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class BenChi implements Car {

    @Override
    public void running() {
        System.out.println("奔驰奔跑。");
    }

}

然后是各自品牌的工厂,在创建各自工厂之前,最好加一层汽车工厂接口,目的是用多态来返回不同的工厂,使代码更加灵活。

汽车工厂接口:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public interface CarFactory {

    Car createCar();

}

各自的汽车工厂类:

宝马工厂:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class BaoMaFactory implements CarFactory {

    @Override
    public Car createCar() {
        System.out.println("宝马工厂生产汽车。");
        return new BaoMa();
    }

}

奔驰工厂:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class BenChiFactory implements CarFactory {

    @Override
    public Car createCar() {
        System.out.println("奔驰工厂生产汽车。");
        return new BenChi();
    }

}

测试类:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class Test01 {

    @Test
    public void m1() {
        // 1. 先获取宝马车的工厂,如果想改奔驰的,仅改动一行代码即可。
        CarFactory carFactory = new BaoMaFactory();
        // 2. 利用工厂来制造汽车
        Car car = carFactory.createCar();
        car.running();
    }

}

工厂方法模式就很利于扩展,且不需要改动原来的代码,不管增加多少个品牌,只需要增加对应的工厂,以及实例就好了。

适用场景:

  1. 客户端不需要知道它所创建的对象的类。例子中我们不知道每个汽车具体叫什么名,只知道创建它的工厂名就完成了创建过程。
  2. 客户端可以通过子类来指定创建对应的对象。

AbstractFactory

抽象工厂的局限性比较大,并且它不符合OCP原则,工厂方法模式是创建一个对象,而抽象工厂它可以创建一组对象,这是和工厂方法模式最大的不同点。上述汽车的例子,假如汽车生产出来,出厂之前,我还需要给汽车做一个包装保养的服务,比如宝马有宝马的包装保养,奔驰有奔驰的包装保养,那是不是应该再创建一个专门做保养的工厂呢,肯定不是,抽象工厂的作用就是将汽车的生产、保养,以及后续的任务都做到一起,秉承一条龙服务,包括汽车的生产,包装,保养,维修等。

首先定义基本接口:

  • 汽车公共接口Car:
/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public interface Car {

    /**
     * car在跑。
     */
    void running();
    
}
  • 汽车维修服务接口CarFix:
/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public interface CarFix {

    /**
     * 汽车维修服务
     */
    void carFix(Car car);

}
  • 汽车包装服务接口CarPack:
/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public interface CarPack {

    /**
     * 汽车包装服务
     */
    void carPack(Car car);

}

三个接口的实现类:

  • Car类

宝马BaoMa:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class BaoMa implements Car {

    @Override
    public void running() {
        System.out.println("宝马飞驰。");
    }

}

奔驰BenChi:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class BenChi implements Car {

    @Override
    public void running() {
        System.out.println("奔驰奔跑。");
    }

}
  • 维修类CarFixService:
/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class CarFixService implements CarFix {

    @Override
    public void carFix(Car car) {
        System.out.println(car.getClass() + "在维修。");
    }

}
  • 包装类CarPackService:
/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class CarPackService implements CarPack {

    @Override
    public void carPack(Car car) {
        System.out.println(car.getClass() + "精美包装。");
    }

}

抽象工厂:

抽象工厂中提供了此工厂所有的服务以及功能,包括汽车生产、包装、维修等。

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public interface AbstractFactory {

    /**
     * 汽车创造
     */
    Car createCar();

    /**
     * 汽车修理服务
     */
    CarFix carFix(Car car);

    /**
     * 汽车包装服务
     */
    CarPack carPack(Car car);

}

具体的汽车工厂:

奔驰工厂BenChiFactory:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class BenChiFactory implements AbstractFactory {

    @Override
    public Car createCar() {
        return new BenChi();
    }

    @Override
    public CarFix carFix(Car car) {
        return new CarFixService();
    }

    @Override
    public CarPack carPack(Car car) {
        return new CarPackService();
    }
}

宝马工厂BaoMaFactory:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class BaoMaFactory implements AbstractFactory {

    @Override
    public Car createCar() {
        return new BaoMa();
    }

    @Override
    public CarFix carFix(Car car) {
        return new CarFixService();
    }

    @Override
    public CarPack carPack(Car car) {
        return new CarPackService();
    }

}

测试类:

/**
 * Description:
 * @From www.zhangjianbing.com
 * @Author 70KG
 * @Date 2019/3/11
 * @Since v1.0
 */
public class Test01 {

    @Test
    public void m1() {
        // 1. 先按名字获取工厂
        AbstractFactory factory = new BenChiFactory();
        // 2. 生产车
        Car car = factory.createCar();
        // 3. 包装车
        CarPack carPack = factory.carPack(car);
        carPack.carPack(car);
        // 4. 上路
        car.running();
        // 5. 修理车
        CarFix carFix = factory.carFix(car);
        carFix.carFix(car);
        // -- 以上全部都是由工厂来完成。
    }

}

测试结果:

生产奔驰。
BenChi精美包装。
奔驰奔跑。
BenChi在维修。

抽象工厂适用场景:

  1. 和工厂方法一样客户端不需要知道它所创建的对象的类。
  2. 需要一组对象共同完成某种功能时。并且可能存在多组对象完成不同功能的情况。
  3. 系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,132评论 6 523
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,457评论 3 404
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,411评论 0 368
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,147评论 1 301
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,145评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,611评论 1 315
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,962评论 3 429
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,948评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,479评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,514评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,640评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,228评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,973评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,402评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,551评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,210评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,714评论 2 366