创建者模式-工厂模式

suaxi
2022-08-27 / 0 评论 / 36 阅读 / 正在检测是否收录...
(1)概述

以咖啡店点餐系统为例:

7.1工厂模式.png

当需要添加不同类型的咖啡的时候,需要修改原有的代码,违背了开闭原则,此时如果使用工厂来生产不同类型的咖啡,则只需要与工厂产生对接,工厂模式的最大优点即解耦


(2)简单工厂模式

简单工厂模式不属于GOF的23种经典设计模式,其更像是一种编程习惯

结构
  • 抽象产品:定义产品的规范,描述产品的主要特性和功能
  • 具体产品:实现或继承抽象产品的子类
  • 具体工厂:提供创建产品的方法,调用者通过该方法来获取产品
实现

7.2简单工厂模式.png

工厂处理创建对象的细节,一旦有了 SimpleCoffeeFactory 工厂类,CoffeeStore 类中的 orderCoffee() 就变成此对象的客户,后期如果需要 Coffee 对象直接从工厂获取即可,解除了和 Cooffee 实现类的耦合,但同时又产生了新的耦合:CoffeeStore 对象与 SimpleCoffeeFactory 工厂对象,SimpleCoffeeFactory 工厂对象 与 生产的商品对象

CoffeeStore

public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        //通过简单工厂生产咖啡
        SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
        Coffee coffee = factory.createCoffee(type);

        //加配料
        coffee.addSugar();
        coffee.addMilk();
        return coffee;
    }
}

SimpleCoffeeFactory

public class SimpleCoffeeFactory {

    public Coffee createCoffee(String type) {
        //声明coffee类型的变量,根据不同类型创建不同的coffee子类
        Coffee coffee = null;
        if ("american".equals(type)) {
            coffee = new AmericanCoffee();
        } else if ("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("该类型的咖啡暂未上架");
        }
        return coffee;
    }
}

Coffee

public abstract class Coffee {

    /**
     * 获取咖啡名字
     *
     * @return
     */
    public abstract String getName();

    public void addSugar() {
        System.out.println("加糖");
    }

    public void addMilk() {
        System.out.println("加奶");
    }
}

AmericanCoffee

public class AmericanCoffee extends Coffee {

    @Override
    public String getName() {
        return "美式咖啡";
    }
}

LatteCoffee

public class LatteCoffee extends Coffee {

    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}

Client

public class Client {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("american");
        System.out.println(coffee.getName());
    }
}
优缺点
  • 封装了创建对象的过程,可以通过参数直接获取对象,把对象的创建与业务逻辑分开,避免了修改客户端代码,如果要添加新的产品则直接修改工厂类
  • 增加新产品时需要修改原有的工厂类,违背了开闭原则
扩展(静态工厂)
public class SimpleCoffeeFactory {

    public static Coffee createCoffee(String type) {
        //声明coffee类型的变量,根据不同类型创建不同的coffee子类
        Coffee coffee = null;
        if ("american".equals(type)) {
            coffee = new AmericanCoffee();
        } else if ("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
            throw new RuntimeException("该类型的咖啡暂未上架");
        }
        return coffee;
    }
}


(2)工厂方法模式
概念

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象,工厂方法使一个产品类的实例化延迟到其工厂的子类

结构
  • 抽象工厂:提供创建产品的接口,调用者通过它访问具体工厂的方法来创建产品
  • 具体工厂:实现抽象工厂中的抽象方法,完成具体产品的创建
  • 抽象产品:定义产品的规范,描述产品的主要功能和特性
  • 具体产品:实现抽象产品定义的接口,由具体工厂来创建(与具体工厂一一对应)
实现

7.3工厂方法模式.png

工厂方法模式是简单工厂模式的进一步抽象,由于使用了多态,既保持了简单工厂模式的优点,同时也解决了它的缺点

抽象工厂
CoffeeFactory

public interface CoffeeFactory {

    /**
     * 创建咖啡对象
     *
     * @return
     */
    Coffee createCoffee();
}

具体工厂
AmericanCoffeeFactory

public class AmericanCoffeeFactory implements CoffeeFactory {

    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

LatteCoffeeFactory

public class LatteCoffeeFactory implements CoffeeFactory {

    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

咖啡店类
CoffeeStore

public class CoffeeStore {

    private CoffeeFactory factory;

    public void setFactory(CoffeeFactory factory) {
        this.factory = factory;
    }

    public Coffee orderCoffee() {
        Coffee coffee = factory.createCoffee();
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}

Coffee

public abstract class Coffee {

    /**
     * 获取咖啡名字
     *
     * @return
     */
    public abstract String getName();

    public void addSugar() {
        System.out.println("加糖");
    }

    public void addMilk() {
        System.out.println("加奶");
    }
}

AmericanCoffee

public class AmericanCoffee extends Coffee {

    @Override
    public String getName() {
        return "美式咖啡";
    }
}

LatteCoffee

public class LatteCoffee extends Coffee {

    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}

Client

public class Client {
    public static void main(String[] args) {
        //咖啡店
        CoffeeStore store = new CoffeeStore();
        //咖啡工厂
        //AmericanCoffeeFactory factory = new AmericanCoffeeFactory();
        LatteCoffeeFactory factory = new LatteCoffeeFactory();
        store.setFactory(factory);
        //点咖啡
        Coffee coffee = store.orderCoffee();
        System.out.println(coffee.getName());
    }
}
优缺点
  • 用户只需要知道具体工厂的名称就能得到需要的产品,无需关心产品的具体创建过程
  • 增加新的产品时只需添加具体产品类和对应的工厂类,无须对原工厂进行修改,满足开闭原则
  • 每增加一个产品就要增加一个具体产品类和对应的工厂类,增加了系统的复杂度


(3)抽象工厂模式
概念

为访问类提供一个创建一组或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构

结构
  • 抽象工厂:提供创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品
  • 具体工厂:主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建
  • 抽象产品:定义产品的规范,描述产品的主要功能和特性(抽象工厂模式具有多个抽象产品)
  • 具体产品:实现抽象产品角色所定义的接口,由具体工厂来创建,它与具体工厂之间是多对一的关系
实现

当咖啡店业务发生变化时,不仅要上架咖啡,还需要上架甜点(提拉米苏、抹茶慕斯),按照工厂方法模式,很容易发生类爆炸的问题;其中:

  • 拿铁、美式属于一个产品等级,都是咖啡;
  • 提拉米苏、抹茶慕斯属于一个产品等级,都是甜点;
  • 拿铁、提拉米苏属于一个产品类别,都是意大利风味;
  • 美式、抹茶慕斯属于一个产品类别,都是美式风味

可以使用抽象工厂模式来实现:

1.4抽象工厂模式.png

抽象工厂

public interface DessertFactory {

    /**
     * 生产咖啡
     *
     * @return
     */
    Coffee createCoffee();

    /**
     * 生产甜品
     *
     * @return
     */
    Dessert createDessert();
}

具体工厂

public class AmericanDessertFactory implements DessertFactory {

    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new MatchaMousse();
    }
}

public class ItalyDessertFactory implements DessertFactory {

    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }

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

如果要添加同一个产品族的话,只需添加一个对应的甜品工厂类,满足开闭原则

public abstract class Coffee {

    /**
     * 获取咖啡名字
     *
     * @return
     */
    public abstract String getName();

    public void addSugar() {
        System.out.println("加糖");
    }

    public void addMilk() {
        System.out.println("加奶");
    }
}
public abstract class Dessert {

    /**
     * 展示
     */
    public abstract void show();
}
public class Tiramisu extends Dessert {

    @Override
    public void show() {
        System.out.println("提拉米苏");
    }
}

public class LatteCoffee extends Coffee {

    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}
public class MatchaMousse extends Dessert {

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

public class AmericanCoffee extends Coffee {

    @Override
    public String getName() {
        return "美式咖啡";
    }
}
public class Client {
    public static void main(String[] args) {
        //意大利风味甜品工厂
        ItalyDessertFactory factory = new ItalyDessertFactory();
        //拿铁
        Coffee coffee = factory.createCoffee();
        //提拉米苏
        Dessert dessert = factory.createDessert();

        System.out.println(coffee.getName());
        dessert.show();
    }
}
优缺点
  • 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
  • 当产品族中需要增加一个新的产品时,所有的工厂类都需要需改
使用场景
  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如生产厂商的电视机、洗衣机、空调等
  • 系统中有多个产品族,但每次只使用其中的一个产品族,如某人只喜欢某一品牌的服装
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构,如输入法更换皮肤


(4)模式扩展

简单工厂 + 配置文件解耦

在工厂类中加载配置文件的全类名,并创建对象进行存储,客户端如果需要对象,直接获取即可

a. 定义配置文件

bean.properties

american=com.sw.设计模式.创建者模式.factory.configFactory.AmericanCoffee
latte=com.sw.设计模式.创建者模式.factory.configFactory.LatteCoffee

b. 改进工厂类

public class CoffeeFactory {

    //加载配置文件,获取配置文件中配置的全类名,并创建该类的对象进行存储
    //1.定义容器对象存储咖啡
    private static HashMap<String, Coffee> map = new HashMap<>();

    //2.加载配置文件
    static {
        //创建properties对象
        Properties p = new Properties();
        //调用load()方法进行加载
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            p.load(is);
            //从p集合中获取全类名并创建对象
            for (Object key : p.keySet()) {
                String className = p.getProperty((String) key);
                Class<?> clazz = Class.forName(className);
                Coffee coffee = (Coffee) clazz.newInstance();
                //存储
                map.put((String) key, coffee);
            }
        } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 根据名称获取对象
     *
     * @param name 名称
     * @return
     */
    public static Coffee createCoffee(String name) {
        return map.get(name);
    }
}

使用静态成员变量来存储创建的对象,在静态代码块中读取配置文件并创建对象

public class Client {
    public static void main(String[] args) {
        Coffee coffee = CoffeeFactory.createCoffee("american");
        //美式咖啡
        System.out.println(coffee.getName());

        System.out.println("=============================");

        Coffee coffee1 = CoffeeFactory.createCoffee("latte");
        //拿铁咖啡
        System.out.println(coffee1.getName());
    }
}
0

评论 (0)

取消