Java设计模式-创建者模式-抽象工厂模式

岳庆锦

发布于 2022.03.18 00:20 阅读 1790 评论 0

抽象工厂模式

 在介绍抽象工厂模式之前,我们先解释即将涉及到的两个名词:“同等级产品”和“产品族”。

 同等级产品:同种类的产品。

例如,运动鞋、皮鞋、高跟鞋就是同种类的产品,它们都属于鞋子这一类。

 产品族:位于不同等级的一组产品称为一个产品族。

可以理解为同一品牌或同一风格的产品,例如,运动风的上衣、裤子和鞋子分别属于不同等级的产品(上衣类、裤子类、鞋子类),但它们组合起来就是一个产品族(都属于运动风)。

============================================================================================(分割线)

 “抽象工厂模式”和“工厂方法模式”的区别:“工厂方法模式”考虑到的是一类产品的生产(同等级产品的生产)。“抽象工厂模式”考虑的是多等级(多种类)产品的生产。

 简单理解就是,工厂方法模式只生产一个等级的产品,而抽象工厂模式可以生产多个等级的产品。

 举个栗子:在用“工厂方法模式”实现的“点咖啡”案例中,咖啡工厂只生产咖啡,咖啡店也只卖咖啡。但是,现实生活中,大多数工厂是综合型的工厂,能生产多等级(种类)的产品。也就是说,咖啡店既可以卖咖啡,也可以卖甜点,这就涉及到多等级产品,使用“抽象工厂模式”就可以实现这一点。

 概念:“抽象工厂模式”是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

 结构:(从角色名上看,和“简单工厂模式”没有不同,但抽象工厂角色的定义发生了变化)

 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。(同一品牌或风格的产品族包含多个不同等级的产品)

 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。

 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。

 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

 应用:继续改进“点咖啡”案例,使咖啡店可以既卖咖啡又卖甜点。

 分析:现在咖啡店业务发生变化,不仅要生产咖啡,还要生产甜点,如提拉米苏、抹茶慕斯等。

 按同一等级分:拿铁咖啡和美式咖啡是一个产品等级,都是咖啡;提拉米苏和抹茶慕斯是一个产品等级,都是甜点。

 按同一产品族分:拿铁咖啡和提拉米苏是同一产品族(意大利风味);美式咖啡和抹茶慕斯是同一产品族(美式风味)。

 所以,我们可以根据风味不同,创建两个具体工厂,分别是意大利风甜品工厂(ItalyDessertFactory)和美式甜品工厂(AmericanDessertFactory),它们向上抽取出一个共同的父类抽象工厂,甜品工厂(DessertFactory),甜品工厂中有着生产咖啡和生产甜点的方法,让具体工厂根据自己的风格,生产属于自己风格的咖啡和甜点。

咖啡店类(Coffee)、美式咖啡(AmericanCoffee)和拿铁咖啡(LatteCoffee)和之前一样,这里就不重复了。

1.甜点接口(Dessert,抽象产品),包含展示甜点名称的show()方法。

public abstract class Dessert {
    //展示甜点名称
    public abstract void show();
}

2.抹茶慕斯类(MatchaMousse,具体产品),实现父类中展示产品的抽象方法。

public class MatchaMousse extends Dessert{
    @Override
    public void show() {
        System.out.println("抹茶慕斯");
    }
}

3.提拉米苏类(Trimisu,具体产品),实现父类中展示产品的抽象方法。

public class Trimisu extends Dessert{
    //提拉米苏类
    @Override
    public void show() {
        System.out.println("提拉米苏");
    }
}

 

以上三个类的形式和咖啡类相似,在这三种工厂模式中都可复用,需要重点关注和理解的是下面提到的相比之前代码发生改变的类。

 

1.甜品工厂接口(DessertFactory,抽象工厂),包含生产咖啡和生产甜点的抽象方法。

public interface DessertFactory {

    //生产咖啡的功能
    Coffee creatCoffee();

    //生产甜品的功能
    Dessert creatDessert();
}

2.美式风味的甜品工厂(AmericanDessertFactory,具体工厂),生产美式咖啡和抹茶慕斯。

public class AmericanDessertFactory implements DessertFactory{

    //生产美式咖啡
    @Override
    public Coffee creatCoffee() {
        return new AmericanCoffee();
    }

    //生产抹茶慕斯
    @Override
    public Dessert creatDessert() {
        return new MatchaMousse();
    }
}

3.意大利风味的甜品工厂(LatteDessertFactory,具体工厂),生产拿铁咖啡和提拉米苏。

public class ItalyDessertFactory implements DessertFactory{
    //生产拿铁咖啡
    @Override
    public Coffee creatCoffee() {
        return new LatteCoffee();
    }

    //生产提拉米苏
    @Override
    public Dessert creatDessert() {
        return new Trimisu();
    }
}

4.咖啡店类(CoffeeStore),依赖于抽象工厂(符合依赖倒转原则),实现售卖同一风味咖啡和甜点的功能。

public class CoffeeStore {

    //声明私有工厂对象
    private DessertFactory factory;

    public void setFactory(DessertFactory factory){
        this.factory = factory;
    }
    
    //点咖啡功能
    public Coffee orderCoffee(){
        Coffee coffee = factory.creatCoffee();
        //加配料
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }

    //点甜点功能
    public Dessert orderDessert(){
        Dessert dessert = factory.creatDessert();
        return dessert;
    }
}

5.测试类(Client),以点意大利风味的餐点为例。

public class Client {
    public static void main(String[] args) {

        //创建咖啡店对象
        CoffeeStore store = new CoffeeStore();

        //创建意大利风味甜品工厂对象
        ItalyDessertFactory factory = new ItalyDessertFactory();
        //AmericanDessertFactory factory = new AmericanDessertFactory();
        store.setFactory(factory);

        //获取意大利风味咖啡
        Coffee coffee = store.orderCoffee();
        System.out.println(coffee.getName());

        //获取意大利风味甜点
        Dessert dessert = store.orderDessert();
        dessert.show();
    }
}

6.运行结果

 总结分析:在抽象工厂模式中会体现“开闭原则”的倾斜性

 解释:在抽象工厂模式中,增加新的产品族很方便但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质称为“开闭原则”的倾斜性

 增加产品族:只需要增加具体产品,并增加对应的具体工厂类,在不修改原有代码的情况下完成了扩展,遵循了“开闭原则”

 举个栗子,我们的咖啡店推出了肯尼亚风味的餐点,在代码上只需新增肯尼亚咖啡类(KenyaCoffee,肯尼亚风味的咖啡,具体产品),让其实现抽象咖啡父类、焦糖布丁类(CaramelPudding,搭配肯尼亚咖啡的甜点,具体产品),让其实现甜点接口,专门生产肯尼亚风味甜品的工厂(KenyaDessertFactory,具体工厂),让其实现抽象甜品工厂接口。

 增加产品族后的类图

 增加新的产品等级结构(新的产品种类):需要修改所有的工厂角色,包括抽象工厂角色。在所有的工厂中都要新增生产新产品的方法,违背了“开闭原则”

 再举个栗子,还是咖啡店,我们咖啡店这会新增售卖零食(新的产品种类)的功能,此时在原有代码上需要做出的改变就是,新增零食接口(Snaks)及其子类(具体产品): Miss Vickie's薯片 (Miss Vickie's potato chips,美式风味零食)、 Gran cereale饼干 (Gran cereale biscuit,意大利风味零食),在抽象工厂中新增生产零食的抽象方法,作为抽象工厂子类的各个具体工厂势必也要增加并实现生产零食的方法,结果就是修改了原有的代码,违背了开闭原则。

 

 

“抽象工厂方法”优缺点:

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

 缺点:当产品族中需要增加一个新的产品时,所有工厂类都需要进行修改,违背了“开闭原则”。

 使用场景

①当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。

②系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。

③系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。