设计模式:工厂模式


任何可以生产对象的方法或类,都可以称为工厂。单例模式也是一种工厂。

1. 工厂模式(静态工厂模式/简单工厂模式)

先来看看下面的例子:汉堡店卖汉堡


假设目前有三种种类的汉堡:鸡腿汉堡、牛肉汉堡、鲜虾汉堡。可以使用一个通用接口:

public abstract class Burger {
    String name;

    void machining() {
        System.out.println("制作" + this.name + "中");
    }

    void boxing() {
        System.out.println("包装" + this.name + "中");
    }

    void end() {
        System.out.println("完毕");
    }
}
public class ChickenBurger extends Burger {
    public ChickenBurger() {
        this.name = "鸡腿汉堡";
    }
}
public class BeefBurger extends Burger {
    public BeefBurger() {
        this.name = "牛肉汉堡";
    }
}
public class ShrimpBurger  extends Burger {
    public ShrimpBurger() {
        this.name = "鲜虾汉堡";
    }
}

现在商店卖汉堡:

public class BurgerStore {
    public void orderBurger(String name) {
        Burger burger = null;

        // 这里就假设输入的name一定是这三种之一
        if(name.equals("鸡腿汉堡")) {
            burger = new ChickenBurger();
        } else if(name.equals("牛肉汉堡")) {
            burger = new BeefBurger();
        } else if(name.equals("鲜虾汉堡")) {
            burger = new ShrimpBurger();
        }

        burger.machining();
        burger.boxing();
        burger.end();
    }

}

客户端:

public class Client {
    public static void main(String[] args) {
        BurgerStore burgerStore = new BurgerStore();
        burgerStore.orderBurger("牛肉汉堡");
        burgerStore.orderBurger("鸡腿汉堡");
    }
}

上面的全部代码是没问题的,现在来新增汉堡种类,首先是添加类,然后得去修改BurgerStore类的这段代码:

        if(name.equals("鸡腿汉堡")) {
            burger = new ChickenBurger();
        } else if(name.equals("牛肉汉堡")) {
            burger = new BeefBurger();
        } else if(name.equals("鲜虾汉堡")) {
            burger = new ShrimpBurger();
        }

问题就出现在这,不是说这段代码不行,而是设计原则的问题,根据《HeadFirst设计模式》中提出的原则:“找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。”。

现在就把这段代码封装起来,封装后的类就专门来创建汉堡种类,称为简单工厂类:

public class SimpleBurgerFactory {
    public Burger createBurger(String name) {
        Burger burger = null;
        if(name.equals("鸡腿汉堡")) {
            burger = new ChickenBurger();
        } else if(name.equals("牛肉汉堡")) {
            burger = new BeefBurger();
        } else if(name.equals("鲜虾汉堡")) {
            burger = new ShrimpBurger();
        }
        return burger;
    }
}

需要修改BurgerStore类的代码:

public class BurgerStore {

    SimpleBurgerFactory simpleBurgerFactory;

    public BurgerStore(SimpleBurgerFactory simpleBurgerFactory) {
        this.simpleBurgerFactory = simpleBurgerFactory;
    }

    public void orderBurger(String name) {
        Burger burger = simpleBurgerFactory.createBurger(name);

        burger.machining();
        burger.boxing();
        burger.end();
    }

}

至此,这样做的好处:以后都不用去修改BurgerStore类,每次新增汉堡种类时,只需要修改SimpleBurgerFactory类。这样难道有什么不同???把这段代码独立出来,因为可能也有别的商店卖汉堡,而且都是来自同一个工厂(厂商)的,所以这段代码就可以复用

简单工厂模式的创建对象方法可以是静态方法也可以是普通方法。

来看一张类关系图(UML图):

在这里插入图片描述

根据《HeadFirst设计模式》中的说法,简单工厂模式并不是一种真正的设计模式,而是一种编程习惯

2. 工厂方法模式

现在我们来更改下需求:要求按品牌店来分汉堡种类,比如肯德基的鸡腿堡和麦当劳的鸡腿堡。(现实中汉堡种类不一定同时存在在这些品牌店中,纯属虚构),现在假设有肯德基和麦当劳两个品牌,因为肯德基的鸡腿堡和麦当劳的鸡腿堡是不同的,所以不能用原来的ChickenBurger类,得创建各自的ChickenBurger类。

一种做法就是直接在简单工厂中进行修改:

public class SimpleBurgerFactory {
    public Burger createBurger(String type, String name) {
        Burger burger = null;
        if(type.equals("肯德基")) {
            if(name.equals("鸡腿汉堡")) {
                burger = new KFCChickenBurger();  // 前面加个KFC,表示是肯德基的产品
            } else if(name.equals("牛肉汉堡")) {
                burger = new KFCBeefBurger();
            } else if(name.equals("鲜虾汉堡")) {
                burger = new KFCShrimpBurger();
            }
        } else if(type.equals("麦当劳")) { 
            if(name.equals("鸡腿汉堡")) {
                burger = new MChickenBurger(); // 前面加个M,表示是麦当劳的产品
            } else if(name.equals("牛肉汉堡")) {
                burger = new MBeefBurger();
            } else if(name.equals("鲜虾汉堡")) {
                burger = new MShrimpBurger();
            }
        }
        
        return burger;
    }
}

来想一想,把所有实现放在一个工厂类中,要是该工厂类不能正常工作,那么想创建任意一个汉堡种类也可能会出错,也就是耦合度高。而且另一个问题就是,跟上面说的一样,每当新增品牌时,这段代码总是会变化,我们需要找出不变的,封装变的。

现在来看看另一种实现方式,把createBurger() 移到BurgerStore类中,然后createBurger()和BurgerStore都变成抽象的,然后每个品牌去继承BurgerStore类,实现createBurger(),看代码:

为了方便我省略KFCBeefBurger、KFCShrimpBurger、MChickenBurger、MBeefBurger、MShrimpBurger这些产品类,就上面的那些类,前面加个KFC和M,里面就打印加个KFC和M,就拿KFCChickenBurger做例子,其他类似:

public class KFCChickenBurger extends Burger {
    public KFCChickenBurger() {
        this.name = "(KFC)鸡腿汉堡";
    }
}

重点来了:

public abstract class BurgerStore {

    public void orderBurger(String name) {
        Burger burger = createBurger(name);

        burger.machining();
        burger.boxing();
        burger.end();
    }
	// 设置称protected,为了只让子类使用
    protected abstract Burger createBurger(String name);
}

现在来实现两个具体的品牌:

public class KFCBurgerStore extends BurgerStore {
    @Override
    protected Burger createBurger(String name) {
        Burger burger = null;
        if(name.equals("鸡腿汉堡")) {
            burger = new KFCChickenBurger();
        } else if(name.equals("牛肉汉堡")) {
            burger = new KFCBeefBurger();
        } else if(name.equals("鲜虾汉堡")) {
            burger = new KFCShrimpBurger();
        }
        return burger;
    }
}
public class MBurgerStore extends BurgerStore {
    @Override
    protected Burger createBurger(String name) {
        Burger burger = null;
        if(name.equals("鸡腿汉堡")) {
            burger = new MChickenBurger();
        } else if(name.equals("牛肉汉堡")) {
            burger = new MBeefBurger();
        } else if(name.equals("鲜虾汉堡")) {
            burger = new MShrimpBurger();
        }
        return burger;
    }
}

客户端:

public class Client {
    public static void main(String[] args) {
        KFCBurgerStore kfcBurgerStore = new KFCBurgerStore();
        kfcBurgerStore.orderBurger("牛肉汉堡");
        kfcBurgerStore.orderBurger("鸡腿汉堡");

        MBurgerStore mBurgerStore = new MBurgerStore();
        mBurgerStore.orderBurger("牛肉汉堡");
        mBurgerStore.orderBurger("鸡腿汉堡");
    }
}

输出:

制作(KFC)牛肉汉堡中
包装(KFC)牛肉汉堡中
完毕
制作(KFC)鸡腿汉堡中
包装(KFC)鸡腿汉堡中
完毕
制作(M)牛肉汉堡中
包装(M)牛肉汉堡中
完毕
制作(M)鸡腿汉堡中
包装(M)鸡腿汉堡中
完毕

现在每当新增品牌,只需要去继承BurgerStore类就行,而要是KFCBurgerStore或者MBurgerStore出现问题都不会互相影响。

现在来看看工厂方法模式和简单工厂的不同:

  • 工厂方法模式是针对接口编程,使用继承去扩展一个类,而简单工厂是去修改代码。
  • 工厂方法模式如果有一个品牌类出问题,其他品牌类不会受影响(解耦合),这些找错误也很容易;而简单工厂如果出错,可能全部都不能正常工作。

UML图:

在这里插入图片描述

虽然UML图比简单工厂模式复杂点,但是是值得的。

这里的KFCBeefBurger、KFCShrimpBurger、MChickenBurger、MBeefBurger、MShrimpBurger等产品类可以再用一个抽象类继承,比如使用KFCBurger、MBurger去继承Burger,然后就使用KFCBurger、MBurger抽象类进行扩展。

3. 抽象工厂模式

现在假设要新增薯条的产品(属于肯德基和麦当劳两种品牌的产品),有一种方法就是:需要再写一个像上面那样的工厂方法模式:FrenchFriesStore。

但是想想,因为是两种品牌下的两种产品(汉堡和薯条),所以需要一个更大的抽象类。现在肯德基就不单单卖汉堡,麦当劳也是。

薯条产品的代码:

public abstract class FrenchFries {
    String name;

    void machining() {
        System.out.println("制作" + this.name + "中");
    }

    void boxing() {
        System.out.println("包装" + this.name + "中");
    }

    void end() {
        System.out.println("完毕");
    }
}
public class KFCFrenchFries extends FrenchFries {
    public KFCFrenchFries() {
        this.name = "(KFC)薯条";
    }
}
public class MFrenchFries extends FrenchFries {
    public MFrenchFries() {
        this.name = "(M)薯条";
    }
}

现在修改上面的BurgerStore类:使得变成一个更大的抽象类

public abstract class Store {
    public void orderBurger(String name) {
        Burger burger = createBurger(name);

        burger.machining();
        burger.boxing();
        burger.end();
    }
	// 新增薯条的订单
    public void orderFrenchFries(String name) {
        FrenchFries frenchFries = createFrenchFries();

        frenchFries.machining();
        frenchFries.boxing();
        frenchFries.end();
    }
    
    protected abstract Burger createBurger(String name);
	// 新增产品,因为两个品牌只有一种薯条所以不带参数
    protected abstract FrenchFries createFrenchFries();
}
public class KFCStore extends Store {
    @Override
    protected Burger createBurger(String name) {
        Burger burger = null;
        if(name.equals("鸡腿汉堡")) {
            burger = new KFCChickenBurger();
        } else if(name.equals("牛肉汉堡")) {
            burger = new KFCBeefBurger();
        } else if(name.equals("鲜虾汉堡")) {
            burger = new KFCShrimpBurger();
        }
        return burger;
    }

    @Override
    protected FrenchFries createFrenchFries() {
        return new KFCFrenchFries();
    }
}
public class MStore extends Store {
    @Override
    protected Burger createBurger(String name) {
        Burger burger = null;
        if(name.equals("鸡腿汉堡")) {
            burger = new MChickenBurger();
        } else if(name.equals("牛肉汉堡")) {
            burger = new MBeefBurger();
        } else if(name.equals("鲜虾汉堡")) {
            burger = new MShrimpBurger();
        }
        return burger;
    }
    

    @Override
    protected FrenchFries createFrenchFries() {
        return new MFrenchFries();
    }
}

客户端:

public class Client {
    public static void main(String[] args) {
        MStore mStore = new MStore();
        mStore.orderBurger("鸡腿汉堡");
        mStore.orderFrenchFries();

        KFCStore kfcStore = new KFCStore();
        kfcStore.orderBurger("牛肉汉堡");
        kfcStore.orderFrenchFries();
    }
}

输出:

制作(M)鸡腿汉堡中
包装(M)鸡腿汉堡中
完毕
制作(M)薯条中
包装(M)薯条中
完毕
制作(KFC)牛肉汉堡中
包装(KFC)牛肉汉堡中
完毕
制作(KFC)薯条中
包装(KFC)薯条中
完毕

这种设计结构就是抽象工厂模式。

抽象工厂模式和工厂方法模式的不同:

  • 抽象工厂模式是一个比较大的范围,比如肯德基的所有产品,苹果的所有产品;而工厂方法模式只是一类产品,比如肯德基的汉堡,苹果手机。

4. 总结

下面转自菜鸟教程:

意图: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类(意思是:不是由我们程序员来指定,把具体类创建死了,而是由客户端根据需要创建具体类)。

主要解决: 主要解决接口选择的问题。

何时使用: 系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

优点: 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点: 虽然说扩展容易,但是要增加一个系列的某一产品,既要在Store抽象类里加代码,又要在具体的里面加代码。而且如果产品很多的话,类也很多,比较复杂。

使用场景:

​ 1、软件换皮肤,一整套一起换。

​ 2、生成不同操作系统的程序。

如果只是一类产品使用工厂方法模式(如果是多个互不相干的产品也可以使用),如果是多类产品使用抽象工厂(当然需要有联系,比如是同一个品牌下的产品)

参考:

《HeadFirst设计模式》

菜鸟教程:工厂模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值