前言
上面的案例中CoffeeStore依赖于美式咖啡类以及拿铁咖啡类,存在耦合,若果后期我们需要添加一种品种的咖啡的话,势必要修改这个类中的代码,这就违背了设计原则的开闭原则,因为我们扩展新功能的时候修改了之前的代码;
在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。
如果我们使用工厂来生产对象,我们就只需要和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦。
简单工厂模式
简单工厂模式是工厂方法模式的“小弟”,它不属于GoF 23种设计模式,但在软件开发中应用也较为频繁,通常将它作为学习其他工厂模式的入门。
结构
简单工厂包含如下角色:
- 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。在之前的案例中Coffee类就是抽象产品
- 具体产品 :实现或者继承抽象产品的子类,如拿铁或者美式咖啡
- 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。
实现
现在使用简单工厂对工厂模式概述中的案例进行改进,类图如下:
可以看出来咖啡类、拿铁、美式咖啡都没有变化,这里只是添加了一个简单咖啡工厂,用来生产咖啡;所以这里的咖啡商店点咖啡功能就不需要去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;
}
}
这里由工厂(factory)处理创建对象的细节,一旦有了SimpleCoffeeFactory,CoffeeStore类中的orderCoffee()就变成此对象的客户,后期如果需要Coffee对象直接从工厂中获取即可。
这样也就解除了和Coffee实现类的耦合,同时又产生了新的耦合,CoffeeStore对象和SimpleCoffeeFactory工厂对象的耦合,工厂对象和商品对象的耦合。
后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory的代码,违反了开闭原则,修改原有的代码。使用到工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。
优缺点
优点:
封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改(还是得修改原有的工厂类代码啊)???,这样就降低了客户代码修改的可能性,更加容易扩展。
缺点:
增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。
扩展
对简单工厂进行扩展为静态工厂
在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它也不是23种设计模式中的。代码如下:只需要将方法加上静态static即可,调用到工厂方法的地方也不需要创建工厂对象了,只需要调用静态方法即可;(比如之前的咖啡商店)
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;
}
}
唯一的改进就是不需要创建工厂对象,直接调用静态方法;