Java 设计模式——工厂方法模式

1.案例分析

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

在这里插入图片描述

在 Java 中,万物皆对象,这些对象都需要创建,如果创建的时候直接 new 该对象,就会对该对象耦合严重,假如要更换对象,所有 new 对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说工厂模式最大的优点就是:解耦。

2.简单工厂模式

简单工厂不是一种设计模式,反而比较像是一种编程习惯。

2.1.结构

简单工厂包含如下角色:

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

2.2.案例实现

现在使用简单工厂对上面案例进行改进,类图如下:
在这里插入图片描述

2.2.1.抽象产品

Coffee.java

public abstract class Coffee {
    
    
    
    public abstract String getName();
    
    //加糖
    public void addSugar(){
    
    
        System.out.println("加糖");
    }
    
    //加奶
    public void addMilk(){
    
    
        System.out.println("加奶");
    }
}

2.2.2.具体产品

AmericanCoffee.java

//美式咖啡
public class AmericanCoffee extends Coffee {
    
    
    @Override
    public String getName() {
    
    
        return "美式咖啡";
    }
}

LatteCoffee.java

//拿铁咖啡
public class LatteCoffee extends Coffee {
    
    
    @Override
    public String getName() {
    
    
        return "拿铁咖啡";
    }
}

2.2.3.具体工厂

SimpleCoffeeFactory.java

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;
    }
}

CoffeeStore.java

public class CoffeeStore {
    
    
    public Coffee orderCoffee(String type){
    
    
        SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
        //调用生产咖啡的方法
        Coffee coffee = factory.createCoffee(type);
    
        //加配料
        coffee.addMilk();
        coffee.addSugar();
    
        return coffee;
    }
}

2.2.4.测试

Clien.java

public class Clien {
    
    
    public static void main(String[] args) {
    
    
        // 1.创建咖啡店类
        CoffeeStore store = new CoffeeStore();
        // 2.点咖啡
        Coffee coffee = store.orderCoffee("american");
        System.out.println(coffee.getName());
    }
}

工厂 (factory) 处理创建对象的细节,一旦有了 SimpleCoffeeFactory,CoffeeStore 类中的 orderCoffee() 就变成此对象的客户,后期如果需要 Coffee 对象直接从工厂中获取即可。这样也就解除了和 Coffee 实现类的耦合,但同时又产生了新的耦合,CoffeeStore 对象和SimpleCoffeeFactory 工厂对象的耦合,工厂对象和商品对象的耦合。后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory 的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。

2.3.优缺点

  • 优点:封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。
  • 缺点:增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。

2.4.扩展

静态工厂:在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它不是 23 种设计模式中的。代码如下:

public class SimpleCoffeeFactory {
    
    
	public static Coffee createCoffee(String type) {
    
    
		Coffee coffee = null;
		if("americano".equals(type)) {
    
    
			coffee = new AmericanoCoffee();
		} else if("latte".equals(type)) {
    
    
			coffee = new LatteCoffee();
		}
		return coffe;
	}
}

3.✨工厂方法模式

3.1.介绍

工厂方法模式 (Factory Method Pattern) 是一种创建型设计模式,它定义了一个用于创建对象的接口,但将具体的对象创建推迟到子类中进行。在工厂方法模式中,我们通过定义一个抽象工厂类(通常是一个接口),其中包含一个创建对象的抽象方法。具体的对象创建则由实现了抽象工厂类的具体工厂类来完成,每个具体工厂类负责实例化一种具体的对象。这样就将对象的创建和使用分离开来,便于系统的扩展和解耦

3.1.结构

工厂方法模式的主要角色:

  • 抽象工厂 (Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂 (Concrete Factory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品 (Abstract Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品 (Concrete Product):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

3.2.案例实现

使用工厂方法模式对上例进行改进,类图如下:
在这里插入图片描述

3.2.1.抽象工厂

CoffeeFactory.java

//抽象工厂,提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
public interface CoffeeFactory {
    
    
    //生产咖啡
    Coffee createCoffee();
}

3.2.2.具体工厂

AmericanCoffeeFactory.java

//具体工厂:生产美式咖啡
public class AmericanCoffeeFactory implements CoffeeFactory{
    
    
    @Override
    public Coffee createCoffee() {
    
    
        return new AmericanCoffee();
    }
}

LatteCoffeeFactory.java

//具体工厂:生产拿铁咖啡
public class LatteCoffeeFactory implements CoffeeFactory{
    
    
    
    @Override
    public Coffee createCoffee() {
    
    
        return new LatteCoffee();
    }
}

CoffeeStore.java

//咖啡店
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;
    }
}

3.2.3.测试

Client.java

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //创建咖啡店对象
        CoffeeStore coffeeStore = new CoffeeStore();
        //创建具体的咖啡店工厂
        CoffeeFactory factory = new AmericanCoffeeFactory();
        //CoffeeFactory factory = new LatteCoffeeFactory();
        coffeeStore.setFactory(factory);
        //点咖啡
        Coffee coffee = coffeeStore.orderCoffee();
        System.out.println(coffee.getName());
    }
}

从以上的编写的代码可以看到,要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且也克服了其缺点。

3.3.优缺点

(1)优点

  • 将对象的创建和使用相分离:工厂方法模式将对象的创建逻辑封装在具体工厂类中,客户端只需通过工厂接口或抽象类来使用产品,无需关心具体的创建过程,从而实现了对象的创建和使用的解耦。
  • 可以轻松添加新的产品:由于每个具体工厂类负责实例化一种具体的产品对象,如果需要添加新的产品,只需要添加一个新的具体工厂类即可,不需要修改现有代码,符合开闭原则。
  • 灵活性好:工厂方法模式可以根据具体需求选择不同的具体工厂类来创建对象,从而实现不同的行为和逻辑。客户端只需要知道工厂接口或抽象类,无需关心具体的实现细节。

(2)缺点

  • 类的个数增加:由于每个具体产品类都需要对应一个具体工厂类,因此如果产品种类较多,会导致具体工厂类的数量增加,增加了系统的复杂性。
  • 增加了系统的抽象性和理解难度:相比于简单工厂模式,工厂方法模式引入了抽象工厂类和具体工厂类之间的层级关系,增加了系统的抽象性和理解难度,需要对抽象工厂、具体工厂及其之间的关系有深入的理解。

3.4.应用场景

(1)工厂方法模式适用于以下应用场景:

  • 对象的创建需要根据不同的条件或参数进行灵活配置:当对象的创建过程比较复杂,需要根据不同的条件或参数来配置对象的创建逻辑时,可以使用工厂方法模式。每个具体工厂类可以根据不同的条件或参数来创建对应的产品对象。
  • 需要扩展或添加新的产品时:当系统需要添加新的产品时,可以通过扩展或添加新的具体工厂类来实现,而无需修改现有的客户端代码。适用于符合开闭原则的情况。
  • 隐藏对象创建的细节:当需要将对象的创建细节隐藏起来,只对外提供一个抽象的工厂接口或抽象类时,可以使用工厂方法模式。客户端只需关心抽象工厂和产品接口,无需关心具体的产品创建过程。
  • 通过工厂方法进行依赖注入:在依赖注入 (Dependency Injection) 的场景中,可以使用工厂方法模式来创建和注入依赖对象。具体工厂类可以负责创建和管理依赖对象的生命周期,并将创建好的对象注入到需要它的类中。

(2)总而言之,工厂方法模式适用于需要根据不同条件创建不同对象、需要动态添加新产品、隐藏对象创建细节或进行依赖注入的场景。

猜你喜欢

转载自blog.csdn.net/weixin_43004044/article/details/131747997