目录
工厂方法模式是一种创建型设计模式,用于创建对象。它的主要目的是通过将实例化逻辑从代码中抽象出来,从而使代码更加灵活和可维护。通过使用工厂模式,我们可以避免在代码中直接实例化对象,而是将对象创建的任务委托给一个专门的工厂类来处理。
在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。 如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象, 直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦。
工厂模式通常有两种实现方式:简单工厂模式和工厂方法模式。简单工厂模式使用一个工厂类来创建所有的对象,而工厂方法模式则将对象的创建委托给具体的工厂子类来处理。两种方式都可以用于创建对象,但工厂方法模式更加灵活,允许我们在运行时动态地选择要创建的对象。
工厂模式的一个常见应用场景是在创建对象时需要进行一些额外的处理或初始化。例如,当创建一个数据库连接对象时,我们可能需要先进行身份验证、设置连接选项等操作。通过使用工厂模式,我们可以将这些额外的操作封装在工厂类中,使得代码更加清晰和易于维护。
简单工厂模式
简单工厂不是一种设计模式,反而比较像是一种编程习惯。简单工厂模式通过一个工厂类来实现对象的创建,工厂类根据调用者的要求来创建不同的对象,从而实现了对象的创建过程和调用者的解耦。
简单工厂模式通常用于创建一些简单的对象,例如日志记录器、数据访问对象等。如果需要创建更复杂的对象,可以考虑使用工厂方法模式、抽象工厂模式或者建造者模式等其他创建型设计模式。
结构
简单工厂包含如下角色:
抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
具体产品 :实现或者继承抽象产品的子类
具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。
实现代码
咖啡店类:
public class CoffeeStore {
public Coffee orderCoffee(String type) {
SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
//调用生产咖啡的方法
Coffee coffee = factory.createCoffee(type);
//加配料
coffee.addMilk();
coffee.addsugar();
return coffee;
}
}
工厂类代码:
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffee;
}
}
咖啡类:
public abstract class Coffee {
public abstract String getName();
//加糖
public void addsugar() {
System.out.println("加糖");
}
//加奶
public void addMilk() {
System.out.println("加奶");
}
}
美式咖啡:
public class AmericanCoffee extends Coffee {
public String getName() {
return "美式咖啡";
}
}
拿铁咖啡类:
public class LatteCoffee extends Coffee {
public String getName() {
return "拿铁咖啡";
}
}
测试类:
public class Client {
public static void main(String[] args) {
//创建咖啡店类对象
CoffeeStore store = new CoffeeStore();
Coffee coffee = store.orderCoffee("latte");
System.out.println(coffee.getName());
}
}
有了SimpleCoffeeFactory,CoffeeStore类中的 orderCoffee()就变成此对象的客户,后期如果需要Coffee对象直接从工厂中获取即可。
这样也就 解除了和Coffee实现类的耦合,同时又产生了新的耦合,CoffeeStore对象和 SimpleCoffeeFactory工厂对象的耦合,工厂对象和商品对象的耦合。 后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory的代码,违反了开闭原则。
工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修 改操作。
优缺点:
简单工厂模式(Simple Factory Pattern)的优点包括:
-
简化对象的创建过程:简单工厂模式可以隐藏对象的创建细节,让客户端代码只关注对象的使用,从而简化对象的创建过程。
-
降低客户端代码的耦合度:客户端代码不需要直接与具体产品类耦合,而是通过工厂类来创建产品对象,从而降低客户端代码的耦合度。
-
提高代码的可维护性:通过引入工厂类来统一创建对象,如果需要修改创建逻辑,只需要修改工厂类中的代码即可,而不需要修改客户端代码,从而提高代码的可维护性。
简单工厂模式的缺点包括:
-
工厂类职责过重:在简单工厂模式中,工厂类通常负责创建所有产品对象,如果产品种类较多,工厂类将会变得十分复杂,职责过重。
-
不符合开闭原则:在简单工厂模式中,如果需要新增一种产品,就需要修改工厂类的代码,这违背了开闭原则。
-
无法满足复杂产品的创建需求:简单工厂模式只能创建单一的产品,对于复杂的产品创建需求,无法满足。
拓展--静态工厂模式
静态工厂模式(Static Factory Pattern)是工厂模式的一种变体,与传统的工厂模式相比,静态工厂模式在创建对象时使用了静态方法,不需要先实例化工厂类,可以直接通过工厂类调用静态方法来创建所需对象。
工厂类:
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;
}
}
商店类:
public class CoffeeStore {
public Coffee orderCoffee(String type) {
Coffee coffee = SimpleCoffeeFactory.createCoffee(type);
//加配料
coffee.addMilk();
coffee.addsugar();
return coffee;
}
}
注意:
由于静态工厂方法是静态的,无法被继承和重写,因此无法通过子类来改变所创建对象的类型。
扩展--简单工厂+配置文件解除耦合
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全 类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。
具体来说,我们可以将工厂类中的对象类型定义为一个配置项,而不是一个具体的类名,然后在配置文件中设置这个配置项的值。这样,客户端代码就只需要通过读取配置文件来获取需要创建的对象类型,然后将这个类型传递给工厂类进行创建,而不需要直接依赖于具体的类名。这样,客户端代码就可以与具体的对象创建逻辑解耦,而只需要关注需要创建的对象类型。
bean.properties:
american=com.itheima.pattern.factory.config_factory.AmericanCoffee
latte=com.itheima.pattern.factory.config_factory.LatteCoffee
CoffeeFactory类:
public class CoffeeFactory {
private static Map<String,Coffee> map = new HashMap();
static {
Properties p = new Properties();
InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
p.load(is);
//遍历Properties集合对象
Set<Object> keys = p.keySet();
for (Object key : keys) {
//根据键获取值(全类名)
String className = p.getProperty((String) key);
//获取字节码对象
Class clazz = Class.forName(className);
Coffee obj = (Coffee) clazz.newInstance();
map.put((String)key,obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Coffee createCoffee(String name) {
return map.get(name);
}
}
静态成员变量用来存储创建的对象(键存储的是名称,值存储的是对应的对象),而读取配置文件以及 创建对象写在静态代码块中,目的就是只需要执行一次。通过将对象类型定义为配置项,我们可以将对象的创建逻辑与客户端代码解耦,从而使系统更加灵活和可维护。
工厂方法模式
工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,但将实际的创建过程推迟到子类中完成。在工厂方法模式中,父类提供了一个用于创建对象的抽象方法,而子类则根据需要实现这个方法来创建具体的对象。
结构
工厂方法模式主要有以下几个角色:
- 抽象工厂(Creator):定义一个抽象的工厂方法,由子类实现具体的对象创建。
- 具体工厂(ConcreteCreator):实现抽象工厂中定义的抽象工厂方法,用于创建具体的产品对象。
- 抽象产品(Product):定义了产品对象的通用接口。
- 具体产品(ConcreteProduct):实现了抽象产品接口,是具体的产品对象。
代码实现:
抽象工厂:
public interface CoffeeFactory {
Coffee createCoffee();
}
具体工厂:
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
咖啡店类:
public class CoffeeStore {
private CoffeeFactory factory;
public CoffeeStore(CoffeeFactory factory) {
this.factory = factory;
}
public Coffee orderCoffee(String type) {
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addsugar();
return coffee;
}
}
从以上的编写的代码可以看到,要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了, 这样就解决了简单工厂模式的缺点。
工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的 优点,而且克服了它的缺点。
优缺点:
工厂方法模式的优点有:
- 可扩展性好:由于具体产品的创建由子类来完成,因此可以很方便地扩展新的产品类别,同时也不会影响已有代码的稳定性。
- 降低耦合性:工厂方法将产品的创建和使用解耦,客户端只需要关心产品的接口,不需要知道具体的实现细节,从而降低了系统的耦合性。
- 符合单一职责原则:每个具体工厂只负责创建一种产品,符合单一职责原则。
工厂方法模式的缺点有:
- 类的个数增多:引入工厂方法模式会增加系统中类的个数,每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,增加了系统的复杂度。
- 代码实现复杂:每个具体工厂类都需要实现抽象工厂中定义的抽象工厂方法,这会增加代码的实现复杂度。
- 无法避免修改:当需要增加新的产品类别时,除了添加新的具体产品类外,还需要修改抽象工厂和所有的具体工厂类,这会影响到系统的稳定性。
工厂方法模式VS建造者模式
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;
而如果使用建造者模式,则需要组装手、头、脚、躯干 等部分,然后再把内裤外穿,于是一个超人就诞生了。
工厂方法模式和建造者模式都可以用于创建对象,但是它们的应用场景和实现方式有所不同。工厂方法模式适用于创建一组相关或相似的对象,而建造者模式适用于创建复杂对象。