设计模式之工厂模式

程序媛和她的猫

共 17999字,需浏览 36分钟

 ·

2021-03-26 14:11

一、工厂模式简介

1、使用工厂模式的好处

工厂模式是创建型模式中最典型的模式,主要作用是用来创建对象,使用工厂模式替代new操作有如下几个好处:
1:解耦,把创建对象的工作和使用对象的工作分开,比如我们想使用对象A,我们无需关心对象A是如何创建的,直接去工厂获取就行。
2:对于构造函数很复杂的对象,可以很好地对外层屏蔽代码的复杂性
3:把初始化实例的工作放到工厂里进行统一管理,使代码更容易维护
4:修改代码时不会引起太大的变动,如果想改代码的话,只需要改一处即可,良好的扩展性
5:减少代码量,如果构造方法比较复杂,有很多很多个构造函数,需要一连串的代码创建对象,如果使用new就会造成很多重复的代码。

2、用一个例子说明使用工厂模式相对于new操作的优势

下面我们分别从使用new创建对象和工厂模式两种方面讲解,帮助大家理解工厂模式的优势。

需求1:有三个用户,需要定制相同的蓝莓黑巧动物奶油做的蛋糕。使用new创建对象:

// 蛋糕类
public class Cake {
    private String fruit;// 配料:水果
    private String chocolate;// 配料:巧克力
    private String cream;// 配料:奶油

    public Cake(String fruit, String chocolate, String cream){
        this.fruit = fruit;
        this.chocolate = chocolate;
        this.cream = cream;
    }
}
public class Test1 {
    public static void main(String[] args) {
        // 用户A订了一个蓝莓黑巧动物奶油做的蛋糕
        Cake cake1 = new Cake("蓝莓""黑巧克力""动物奶油");
        // 用户B订了一个蓝莓黑巧动物奶油做的蛋糕
        Cake cake2 = new Cake("蓝莓""黑巧克力""动物奶油");
        // 用户C订了一个蓝莓黑巧动物奶油做的蛋糕
        Cake cake3 = new Cake("蓝莓""黑巧克力""动物奶油");
    }
}

工厂模式:

// 蛋糕工厂
public class CakeFactory {
    public Cake createCake(){
        return new Cake("蓝莓""黑巧克力""动物奶油");
    }
}
public class Test2 {
    public static void main(String[] args) {
        CakeFactory cakeFactory = new CakeFactory();
        // 用户A订了一个蓝莓黑巧动物奶油做的蛋糕
        cakeFactory.createCake();
        // 用户B订了一个蓝莓黑巧动物奶油做的蛋糕
        cakeFactory.createCake();
        // 用户C订了一个蓝莓黑巧动物奶油做的蛋糕
        cakeFactory.createCake();
    }
}

需求2;这三个用户想要在蛋糕中加入一种坚果,如果使用new创建对象,需要修改三个new操作,使用工厂模式,只需修改工厂中的new操作即可,改动更少,带来错误的可能性就越少。 使用new创建对象:

// 蛋糕类
public class Cake {
    private String fruit;// 配料:水果
    private String chocolate;// 配料:巧克力
    private String cream;// 配料:奶油
    private String nut;// 配料:坚果

    public Cake(String fruit, String chocolate, String cream, String nut){
        this.fruit = fruit;
        this.chocolate = chocolate;
        this.cream = cream;
        this.nut = nut;
    }
}
public class Test1 {
    public static void main(String[] args) {
        // 用户A订了一个蓝莓黑巧动物奶油榛子做的蛋糕
        Cake cake1 = new Cake("蓝莓""黑巧克力""动物奶油""榛子");
        // 用户B订了一个蓝莓黑巧动物奶油榛子做的蛋糕
        Cake cake2 = new Cake("蓝莓""黑巧克力""动物奶油""榛子");
        // 用户C订了一个蓝莓黑巧动物奶油榛子做的蛋糕
        Cake cake3 = new Cake("蓝莓""黑巧克力""动物奶油""榛子");
    }
}

工厂模式:

// 蛋糕工厂
public class CakeFactory {
    public Cake createCake(){
        return new Cake("蓝莓""黑巧克力""动物奶油""榛子");
    }
}
public class Test2 {
    public static void main(String[] args) {
        CakeFactory cakeFactory = new CakeFactory();
        // 用户A订了一个蓝莓黑巧动物奶油榛子做的蛋糕
        cakeFactory.createCake();
        // 用户B订了一个蓝莓黑巧动物奶油榛子做的蛋糕
        cakeFactory.createCake();
        // 用户C订了一个蓝莓黑巧动物奶油榛子做的蛋糕
        cakeFactory.createCake();
    }
}
3、工厂模式分类

工厂模式,包含三种情况,简单工厂工厂方法抽象工厂,下面我们就来一一介绍一下。

我相信大家都有这样的困惑,目前我所在的项目都在程序开发的过程中,还是有很多的new()操作出现在程序中,并没有通过工厂来创建对象,一方面可能是因为我们自身比较懒,不规范项目的编码形式,另外一方面也是由于项目的进度比较紧,没有那么多的时间去完成工厂的统一创建。

二、简单工厂

我们将用几个例子来逐步讲解简单工厂、工厂方法、抽象工厂,下面我们就开始吧。

1、简单工厂示例

小明在老家开了一家饮品店,提供多种饮品,当有顾客来店里,只需要和服务员说他想喝哪种饮料,服务员便可提供哪种饮料,这就是简单工厂模式,服务员就是这个简单工厂。

// 抽象饮料
public abstract class Drink {
    public abstract void desc();
}

// 苹果汁
public class AppleDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯苹果汁!");
    }
}

// 葡萄汁
public class GrapeDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯葡萄汁!");
    }
}

// 西瓜汁
public class WatermelonDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯西瓜汁!");
    }
}

// 饮料工厂
public class DrinkFactory {
    // name:饮料名字
    public Drink getDrink(String name){
        if("apple".equals(name)){
            return new AppleDrink();
        }else if("watermelon".equals(name)){
            return new WatermelonDrink();
        }else if("grape".equals(name)){
            return new GrapeDrink();
        }
        return null;
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        DrinkFactory drinkFactory = new DrinkFactory();
        // 顾客想要苹果汁
        Drink appleDrink = drinkFactory.getDrink("apple");
        appleDrink.desc();

        // 顾客想要葡萄汁
        Drink grapeDrink = drinkFactory.getDrink("grape");
        grapeDrink.desc();
    }
}
2、简单工厂的适用场景

(1)、工厂类负责创建的对象比较少。
(2)、客户端(应用层)只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心。

3、简单工厂的优点

只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节,工厂类要有必要的判断逻辑(if else),可以决定在什么时候创建哪一个产品的实例,客户端可以免除直接创建对象的责任。

4、简单工厂的缺点

(1)、工厂类的职责相对较重,增加新的产品,需要修改工厂类的判断逻辑(增加if else分支),修改就意味着引入风险,这违反了开闭原则
(2)、当产品很多的时候,这个工厂类就会很复杂,不利于维护和扩展,代码无法阅读,很难交接。
(3)、无法形成基于继承的等级结构。在工厂方法和抽象工厂中,会讲到产品族和产品等级结构,再了解什么是基于继承的等级结构。

三、工厂方法

1、工厂方法示例

后来店里饮料品种越来越多,每增加一款饮料都需要让服务员知晓,否则顾客说的时候,服务员就无法提供(这就是简单工厂中所说的对工厂类的修改)。

于是,小明直接买了饮料机,每种饮料都有自己的饮料机(这个饮料机就是工厂方法中的产品工厂,每个产品都有自己的工厂),顾客想喝什么,就自己去饮料机取,饮品店以后想增加饮料,只要增加饮料机即可(工厂方法相对于简单工厂,更加易于扩展)。

// 抽象饮料
public abstract class Drink {
    public abstract void desc();
}

// 苹果汁
public class AppleDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯苹果汁!");
    }
}

// 葡萄汁
public class GrapeDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯葡萄汁!");
    }
}

// 西瓜汁
public class WatermelonDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯西瓜汁!");
    }
}

// 抽象饮料工厂
public abstract class DrinkFactory {
    public abstract Drink getDrink();
}

// 苹果汁饮料工厂(苹果汁饮料机)
public class AppleDrinkFactory extends DrinkFactory {
    @Override
    public Drink getDrink() {
        return new AppleDrink();
    }
}

// 葡萄汁饮料工厂
public class GrapeDrinkFactory extends DrinkFactory {
    @Override
    public Drink getDrink() {
        return new GrapeDrink();
    }
}

// 西瓜汁饮料工厂
public class WatermelonDrinkFactory extends DrinkFactory {
    @Override
    public Drink getDrink() {
        return new WatermelonDrink();
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        // 顾客想要苹果汁
        DrinkFactory appleDrinkFactory = new AppleDrinkFactory();
        Drink appleDrink = appDrinkFactory.getDrink();
        appleDrink.desc();

        // 顾客想要葡萄汁
        DrinkFactory grapeDrinkFactory = new GrapeDrinkFactory();
        Drink grapeDrink = grapeDrinkFactory.getDrink();
        grapeDrink.desc();
    }
}
2、工厂方法和简单工厂的区别

简单工厂:所有的产品都在一个工厂中生产,在工厂中使用if else根据传入参数判断生产哪种产品,当增加一种产品的时候,就需要修改工厂的if else逻辑。

工厂方法:每种产品都有自己的工厂,当需要增加一种产品时,无需修改现有的工厂,只需要增加一种工厂即可。遵循了开闭原则,对于扩展是开放的,对于修改是关闭的。

3、工厂方法的优点

(1)、用户只需要关心所需产品对应的工厂,无需关心产品的创建细节。
(2)、加入新产品时,只需要再增加对应的工厂,而不用修改已有的工厂,符合开闭原则,提高扩展性。

4、工厂方法的缺点

类爆炸,因为每个产品都要有一个工厂,如果产品很多,那就会有很多的工厂。用上面的例子来描述,如果后续饮品店出了成百上千种饮品,就要在店里放成百上千台饮料机,店里放不下啊。。。

四、抽象工厂

1、抽象工厂示例

至于抽象工厂模式,可以用套餐来理解。小明在后期,除了提供饮品,还给每种饮品搭配了合适的甜点,向顾客提供下午茶套餐,以此来提高销售量。

所以饮品店制定了一套标准(抽象工厂,具体工厂需要遵循的工厂接口),所有的套餐(具体工厂)必须遵循该标准,比如这个抽象标准可能是:1、需要有一杯果汁。2、需要有一块甜点。于是有了第一个套餐(具体工厂):西瓜汁 + 泡芙,我们称之为套餐A,那顾客点套餐A,即可获得刚才所说产品。当然这里面的西瓜汁、泡芙(具体产品对象),也需要遵守相应的产品规则(产品接口),以保证顾客能吃到放心食品。

产品接口如下:

// 饮料抽象类
public abstract class Drink {
    public abstract void desc();
}

// 甜点抽象类
public abstract class Dessert {
    public abstract void desc();
}

具体产品对象如下:

// 苹果汁
public class AppleDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯苹果汁!");
    }
}

// 葡萄汁
public class GrapeDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯葡萄汁!");
    }
}

// 西瓜汁
public class WatermelonDrink extends Drink {
    @Override
    public void desc() {
        System.out.println("这是一杯西瓜汁!");
    }
}

// 泡芙
public class Puff extends Dessert {
    @Override
    public void desc() {
        System.out.println("这是一块泡芙!");
    }
}

// 慕斯蛋糕
public class MousseCake extends Dessert {
    @Override
    public void desc() {
        System.out.println("这是一块慕斯蛋糕!");
    }
}

// 提拉米苏蛋糕
public class Tiramisu extends Dessert {
    @Override
    public void desc() {
        System.out.println("这是一块提拉米苏蛋糕!");
    }
}

抽象工厂如下:

// 下午茶套餐工厂
public interface SetFactory {
    Drink getDrink();
    Dessert getDessert();
}

具体工厂如下:

/**
 * 套餐A的工厂
 * 套餐A提供西瓜汁 + 泡芙
 */

public class ASetFactory implements SetFactory {
    @Override
    public Drink getDrink() {
        return new WatermelonDrink();
    }

    @Override
    public Dessert getDessert() {
        return new Puff();
    }
}

/**
 * 套餐B的工厂
 * 套餐B提供葡萄汁 + 慕斯蛋糕
 */

public class BSetFactory implements SetFactory {

    @Override
    public Drink getDrink() {
        return new GrapeDrink();
    }

    @Override
    public Dessert getDessert() {
        return new MousseCake();
    }
}

/**
 * 套餐C的工厂
 * 套餐C提供苹果汁 + 提拉米苏蛋糕
 */

public class CSetFactory implements SetFactory {
    @Override
    public Drink getDrink() {
        return new AppleDrink();
    }

    @Override
    public Dessert getDessert() {
        return new Tiramisu();
    }
}

客户端如下:

public class Client {
    public static void main(String[] args) {
        // 顾客想要一份A套餐
        System.out.println("------A套餐-------");
        SetFactory aSetFactory = new ASetFactory();
        Drink aDrink = aSetFactory.getDrink();
        Dessert aDessert = aSetFactory.getDessert();
        aDrink.desc();
        aDessert.desc();

        // 顾客想要一份B套餐
        System.out.println("------B套餐-------");
        SetFactory bSetFactory = new BSetFactory();
        Drink bDrink = bSetFactory.getDrink();
        Dessert bDessert = bSetFactory.getDessert();
        bDrink.desc();
        bDessert.desc();
    }
}

在这里我们看到,对于抽象工厂是无需指定具体的果汁和甜点,但是这些果汁、甜点却是相互依赖的,这也是抽象工厂的意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类

3、抽象工厂的优点

将一系列的产品统一到一起创建。

4、抽象工厂的缺点

(1)、规定了所有可能被创建的产品集合,当产品集合中需要扩展新的产品时,需要修改抽象工厂的接口,违反了开闭原则,比如上面的例子,当我们需要在套餐中加入一份小吃时,就需要修改抽象工厂和所有的具体工厂,改动很大。
(2)、增加了系统的抽象性和理解难度。

5、适用场景

强调一系列相关的产品组成一个产品集合一起使用时,此时使用抽象工厂模式。

五:工厂模式总结

我们结合上面饮品店的例子,来总结一下简单工厂、工厂方法、抽象工厂这三种工厂有什么不同吧,方便大家更好地记忆。

简单工厂,就是所有的产品都在这个工厂中生产,用户需要告诉工厂他需要哪个产品,工厂才能给他对应的产品。

简单工厂

工厂方法,就是每种产品都有自己的工厂,用户想要哪个产品,直接去对应的工厂取即可。

工厂方法

抽象工厂,相当于对所有的产品进行分类(分类的维度根据业务场景来决定),同一种类的产品在同一工厂生产,用户想要哪一种类的产品,直接去对应的工厂取即可。

抽象工厂

标注:图中绿色方框表示工厂,橘色方框表示产品。


浏览 24
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报