工厂设计模式

工厂模式

举例概述

需求:设计一个咖啡店点餐系统。

设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。

这个需求很简单,我们可以很快地写出如下的代码:

Coffee抽象类

public abstract class Coffee {
    public abstract void getName();

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

美式咖啡子类

public class AmericanCoffee extends Coffee{
    @Override
    public void getName() {
        System.out.println("美式咖啡");
    }
}

拿铁咖啡子类

public class LatteCoffee extends Coffee{
    @Override
    public void getName() {
        System.out.println("拿铁咖啡");
    }
}

Coffee店

public class CoffeeStore {
    public Coffee orderCoffee(String name) {
        if ("american".equals(name)) {
            return new AmericanCoffee();
        } else if ("latte".equals(name)) {
            return new LatteCoffee();
        } else {
            throw new RuntimeException("该店无此咖啡");
        }
    }
}

主测试方法

public class Test {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("american");
        coffee.getName();
    }
}

image-20230624154506202

需求一

现在产品经理来了,说要多一种咖啡的类型——卡布奇诺,你打开项目,添加Coffee子类,修改咖啡店的代码,添加了一句else if,发现工作量很小,并没有什么不妥的,虽然违反了开闭原则,但是我只是加了一句else if,维护工作不算大。于是你放心地早早交付任务了。

public class CoffeeStore {
    public Coffee orderCoffee(String name) {
        if ("american".equals(name)) {
            return new AmericanCoffee();
        } else if ("latte".equals(name)) {
            return new LatteCoffee();
        } else if ("cappuccino".equals(name)){
            return new Cappuccino();
        } else {
            throw new RuntimeException("该店无此咖啡");
        }
    }
}

简单工厂模式

产品经理又来了,说有多家咖啡店要来合作,所以现在不止是一家CoffeeStore了,要一家美式咖啡店和一家意大利风味的咖啡店,并且还要再加一种新的咖啡类型,你这时就犯了难了,一家CoffeStore一份if else 的代码,多家岂不是多份一模一样的代码了?那么你思来想去,决定加一层,去解耦咖啡店和咖啡之间的关系,然后每家咖啡店都去引用中间一层即可,那么这就是简单工厂模式。

咖啡类都不需要更改,主要改动是咖啡店的代码,并加了一层中间的工厂

咖啡工厂代码

public class SimpleCoffeeFactory {
    public Coffee createCoffee(String name) {
        if ("american".equals(name)) {
            return new AmericanCoffee();
        } else if ("latte".equals(name)) {
            return new LatteCoffee();
        }else {
            throw new RuntimeException("该店无此咖啡");
        }
    }
}

咖啡店

public class CoffeeStore {
    public Coffee orderCoffee(String name) {
        SimpleCoffeeFactory simpleCoffeeFactory = new SimpleCoffeeFactory();
        Coffee coffee = simpleCoffeeFactory.createCoffee(name);
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}

可以看到,现在咖啡店只需要通过工厂去创建咖啡,而选择创建哪一种咖啡的逻辑都放在了工厂去执行,如果现在多了几家咖啡店,也是一样,只需要获取工厂对象,然后创建咖啡即可。

对于工厂中的创建咖啡的代码,可以写为静态的方法,然后直接静态调用即可,该工厂也叫做静态工厂。

工厂方法模式

产品经理又来了,这次又推出了新品,继续让你加一种咖啡类型,你已经快要崩溃了,前前后后加咖啡类型已经几十次了,if else 已经写得满屏都是,你这时候想,能不能直接来多个工厂,然后每个创建一种咖啡,然后后台代码只需要添加新的类,然后修改的地方全部给客服端代码去做,这样就很好地符合了一个开闭原则,说完,就开始动手了,这就是工厂方法模式。

抽象工厂

public interface CoffeeFactoryMethod {
    Coffee createCoffee();
}

工厂实现类

public class AmericanCoffeeFactory implements CoffeeFactoryMethod{
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}
public class LatteCoffeeFactory implements CoffeeFactoryMethod{
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

咖啡店

public class CoffeeStore {
    private CoffeeFactoryMethod coffeeFactory;

    public void setCoffeeFactory(CoffeeFactoryMethod coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

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

测试方法(客户端代码)

public class Test {
    public static void main(String[] args) {
        String name = "american";
        CoffeeStore coffeeStore = new CoffeeStore();
        CoffeeFactoryMethod coffeeFactory = null;
        if ("american".equals(name)) {
            coffeeFactory = new AmericanCoffeeFactory();
        } else if ("latte".equals(name)) {
            coffeeFactory = new LatteCoffeeFactory();
        }
        coffeeStore.setCoffeeFactory(coffeeFactory);
        Coffee coffee = coffeeStore.orderCoffee();
        coffee.getName();
    }
}

现在,后台的代码只需要依次增加工厂和对应咖啡类型,然后其他需要修改的部分都是客户端的了。但是,类的爆炸性增长,也会增加系统的复杂度。

抽象工厂

前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机、传智播客只培养计算机软件专业的学生等。

而抽象工厂就是把同家店的东西全部聚合到一起,比如说,意大利工厂同时生产拿铁咖啡和提拉米苏,美国工厂同时生产美式咖啡和拿铁,然后两个工厂都实现抽象工厂接口。

美国工厂和意大利工厂(其实就是整合了简单工厂和工厂方法模式)

//美式甜点工厂
public class AmericanDessertFactory implements DessertFactory {

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

    public Dessert createDessert() {
        return new MatchaMousse();
    }
}
//意大利风味甜点工厂
public class ItalyDessertFactory implements DessertFactory {

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

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

使用场景

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

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

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

如:输入法换皮肤,一整套一起换(logo、声音、颜色、效果、光效为同一家店生产的)。生成不同操作系统的程序。

反射+配置文件改进简单工厂

代码已经运行好一段时间了,但是你发现,不管怎么改,总是会有一段if else的判断逻辑。

if ("american".equals(name)) {

} else if ("latte".equals(name)) {

}

刚学了反射的你,你突然想到,难道就不能直接通过反射去创建对应的类吗?说着,你就动手实践了起来。

一、定义配置文件

我们先在resource目录下新建一个bean.properties的文件,里面就写你需要自动实例化的类的引用路径,文件内容如下:

image-20230625135312723

american=shop.stopyc.patterns.factory.simplefactory.demo4.AmericanCoffee
latte=shop.stopyc.patterns.factory.simplefactory.demo4.LatteCoffee

二、改进工厂类

public class SimpleCoffeeFactory {

    private static Map<String, Coffee> hashMap =  new HashMap<>();

    static {
        Properties properties = new Properties();
        Class<SimpleCoffeeFactory> aClass = SimpleCoffeeFactory.class;
        InputStream is = aClass.getClassLoader().getResourceAsStream("bean.properties");
        try {
            properties.load(is);
            for (Object key : properties.keySet()) {
                String className = (String) properties.get(key);
                Class<?> clazz = Class.forName(className);
                Coffee coffee = (Coffee) clazz.newInstance();
                hashMap.put((String) key, coffee);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Coffee createCoffee(String name) {
        return hashMap.get(name);
    }
}

三、使用

使用和简单工厂一模一样

public class CoffeeStore {
    public Coffee orderCoffee(String name) {
        Coffee coffee = SimpleCoffeeFactory.createCoffee(name);
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}
public class Test {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("latte");
        coffee.getName();
    }
}

通过反射+配置文件,直接砍去了if else的判断逻辑,现在只要和客服端约定key的值,然后客户端传入key,后台就能自动获取对应的实例。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇