本文已参与「新人创作礼」活动,一起开启掘金创作之路。
耦合度
紧密连接程度,
耦合度低,可扩展性和可维护性提高。
接口
接口能实现拓展功能。
模式意图
将对象的实例化交给工厂,只需要告诉工厂需要什么对象。
让使用者只需要关注自己需要的,而不需要关注这个东西是怎么创建的,让实例创建和实例使用解耦。
用户角度
客户告诉商店需要什么商品,商店提供商品。
简单工厂:输入字符串
工厂方法:定义具体实现类工厂
业务角度
商店根据客户需求,向工厂获取原始商品再进行加工。
简单工厂:将字符串传给工厂,工厂根据字符串值制作商品
工厂方法:传入具体实现类工厂,实现类工厂创建具体商品(工厂与商品一对一)
工厂角度
根据某个标准判断返回的商品
简单工厂:根据字符串判断
工厂方法:具体工厂直接返回具体商品
首先解决业务模块和商品的耦合(即添加商品不需要修改业务的代码),再解除商品和工厂的耦合(添加商品不需要修改工厂的代码)。
接口实现了多态,通过使用接口,消除了原来的判断逻辑,所以可以直接增添新的实现类而不需要修改代码来实现判断逻辑。
引例
public class Factory { public static void main(String[] args) { CoffeeStore coffeeStore = new CoffeeStore(); Coffee coffee = coffeeStore.orderCoffee("american"); System.out.println(coffee.getName()); } } abstract class Coffee{ public abstract String getName(); public void addSugar(){ System.out.println("加糖"); } public void addMilk(){ System.out.println("加奶"); } } class AmericanCoffee extends Coffee{ @Override public String getName() { return "美式咖啡"; } } class LatteCoffee extends Coffee{ @Override public String getName() { return "拿铁咖啡"; } } class CoffeeStore{ public Coffee orderCoffee(String type){ //根据不同类型创建不同咖啡 Coffee coffee = null; if("american".equals(type)){ coffee = new AmericanCoffee(); }else if("latte".equals(type)){ coffee = new LatteCoffee(); } coffee.addSugar(); coffee.addMilk(); return coffee; } }
在本例中,如果要再添加新的咖啡品种的话,势必要修改CoffeeStore类中的代码。
也就是商店类和商品类耦合在了一起,不能直接扩展新的商品。
工厂模式的特点就是解耦。
简单工厂
结构
- 抽象产品
- 具体产品
- 具体工厂
public class Client { public static void main(String[] args) { CoffeeStore coffeeStore = new CoffeeStore(); Coffee coffee = coffeeStore.orderCoffee("latte"); System.out.println(coffee.getName()); } } class CoffeeStore{ public Coffee orderCoffee(String type){ SimpleFactory factory = new SimpleFactory(); Coffee coffee = factory.createCoffee(type); coffee.addMilk(); coffee.addSugar(); return coffee; } } class SimpleFactory{ public Coffee createCoffee(String type){ Coffee coffee = null; if("american".equals(type)){ coffee = new AmericanCoffee(); }else if("latte".equals(type)){ coffee = new LatteCoffee(); } coffee.addSugar(); coffee.addMilk(); return coffee; } } abstract class Coffee{ public abstract String getName(); public void addSugar(){ System.out.println("加糖"); } public void addMilk(){ System.out.println("加奶"); } } class AmericanCoffee extends Coffee{ @Override public String getName() { return "美式咖啡"; } } class LatteCoffee extends Coffee{ @Override public String getName() { return "拿铁咖啡"; } }
虽然通过工厂类解除了商品类和商店的耦合,但是又增加了新的耦合,即商店和工厂耦合,工厂和商品耦合。
如果以后添加新的产品,势必修改工厂类的代码。
与之前代码的区别是,当需要增加多个客户端如甜品店,甜品店内也有咖啡,则只需要修改工厂类就可以,之前的代码则要在店类中修改很多代码,增加很多与咖啡店相同的if判断。
增加新的产品时,不需要在业务部分(商店)修改代码了,但是需要修改工厂类的代码。
静态工厂
将简单工厂中的生产商品的方法修改为静态,在生成商品时不需要创建工厂类了。
工厂方法
结构
- 抽象产品
- 具体产品
- 抽象工厂
- 具体工厂:具体产品与具体工厂一一对应
public class Client { public static void main(String[] args) { CoffeeStore coffeeStore = new CoffeeStore(); coffeeStore.setFactory(new AmericanCoffeeFactory()); Coffee coffee = coffeeStore.orderCoffee(); System.out.println(coffee.getName()); } } class CoffeeStore{ private CoffeeFactory factory; public void setFactory (CoffeeFactory factory){ this.factory = factory; } public Coffee orderCoffee(){ Coffee coffee = factory.createCoffee(); coffee.addSugar(); coffee.addMilk(); return coffee; } } //抽象工厂 interface CoffeeFactory{ Coffee createCoffee(); } class AmericanCoffeeFactory implements CoffeeFactory{ @Override public Coffee createCoffee() { return new AmericanCoffee(); } } class LatteCoffeeFactory implements CoffeeFactory{ @Override public Coffee createCoffee() { return new LatteCoffee(); } } abstract class Coffee{ public abstract String getName(); public void addSugar(){ System.out.println("加糖"); } public void addMilk(){ System.out.println("加奶"); } } class AmericanCoffee extends Coffee{ @Override public String getName() { return "美式咖啡"; } } class LatteCoffee extends Coffee{ @Override public String getName() { return "拿铁咖啡"; } }
编辑
如果想添加一种咖啡的品种,不再需要修改工厂的代码,只需要再创建新的咖啡工厂的实现类。
优点
用户只需要知道具体的工厂就可以得到所需产品。
系统在新增加产品时只需要添加具体产品类和对应的工厂,无需对原工厂进行任何修改。
缺点
每增加一个产品就要增加一个产品类和工厂类,增加了系统的复杂度。
工厂方法模式只考虑生成同级别的产品,如生产咖啡只生成各种各样的咖啡,不会去生产牛奶。
但是现实中,一个工厂往往是生成多种种类的产品。
抽象工厂
抽象工厂模式考虑的是多级别产品的生产,工厂方法是只生成一个等级的产品
编辑
结构
- 抽象工厂:包含多个创建产品的方法,每个方法是同一个产品族中不同产品
- 具体工厂
- 抽象产品
- 具体产品
public class Client { public static void main(String[] args) { CoffeeStore coffeeStore = new CoffeeStore(); coffeeStore.setFactory(new AmericanFactory()); Coffee coffee = coffeeStore.orderCoffee(); System.out.println(coffee.getName()); Dessert dessert = coffeeStore.orderDessert(); dessert.show(); } } class CoffeeStore{ private Factory factory; public void setFactory (Factory factory){ this.factory = factory; } public Coffee orderCoffee(){ Coffee coffee = factory.createCoffee(); coffee.addSugar(); coffee.addMilk(); return coffee; } public Dessert orderDessert(){ Dessert dessert = factory.createDessert(); return dessert; } } //工厂 interface Factory{ Coffee createCoffee(); Dessert createDessert(); } //美式风味工厂 class AmericanFactory implements Factory{ //美式咖啡 @Override public Coffee createCoffee() { return new AmericanCoffee(); } //抹茶慕斯 @Override public Dessert createDessert() { return new MatchaMousse(); } } //意大利风味工厂 class ItalyFactory implements Factory{ //拿铁咖啡 @Override public Coffee createCoffee() { return new LatteCoffee(); } //提拉米苏 @Override public Dessert createDessert() { return new Trimisu(); } } //咖啡 abstract class Coffee{ public abstract String getName(); public void addSugar(){ System.out.println("加糖"); } public void addMilk(){ System.out.println("加奶"); } } //美式咖啡 class AmericanCoffee extends Coffee{ @Override public String getName() { return "美式咖啡"; } } //拿铁咖啡 class LatteCoffee extends Coffee{ @Override public String getName() { return "拿铁咖啡"; } } //甜品 abstract class Dessert{ public abstract void show(); } //提拉米苏 class Trimisu extends Dessert{ @Override public void show() { System.out.println("提拉米苏"); } } //抹茶慕斯 class MatchaMousse extends Dessert{ @Override public void show() { System.out.println("抹茶慕斯"); } }
与工厂方法不同的是,具体的工厂实现是以产品族划分的,而不是以某一类产品划分的,这样知道了某一个工厂,就能生成该工厂能生产的一族产品。
缺点
当需要添加新的产品时,如汉堡,所有的工厂包括工厂接口都需要更新。
使用场景
编辑
输入法换皮肤,一换一整套,各种图标都属于一个工厂类。
模式扩展
利用简单工厂模式+配置文件的方式解除工厂对象和产品之间的耦合,在工厂类中加载配置文件的全类名,并创建对象进行存储,客户端如果需要对象,直接获取即可。
package mode; import java.io.InputStream; import java.util.HashMap; import java.util.Properties; import java.util.Set; public class Client { public static void main(String[] args) { Coffee coffee = CoffeeStore.orderCoffee("latte"); System.out.println(coffee.getName()); } } class CoffeeStore{ public static Coffee orderCoffee(String type){ Coffee coffee = SimpleFactory.createCoffee(type); coffee.addMilk(); coffee.addSugar(); return coffee; } } class SimpleFactory{ //加载配置文件,获取配置文件中配置的全类名,创建类并储存 //1.定义容器对象 private static HashMap<String,Coffee> map = new HashMap<>(); //2.加载配置文件 static { Properties properties = new Properties(); //配置文件读入 InputStream is = SimpleFactory.class.getClassLoader().getResourceAsStream("mode/bean.properties"); try { //加载对应的配置文件 properties.load(is); //遍历配置文件的内容 Set<Object> keys = properties.keySet();//获取配置文件的所有键名 for(Object key:keys){ String className = properties.getProperty((String) key);//获取配置文件值 Class<?> aClass = Class.forName(className);//获取字节码对象 Coffee coffee = (Coffee) aClass.newInstance();//创建对象 map.put((String) key,coffee); } } catch (Exception e) { e.printStackTrace(); } } public static Coffee createCoffee(String name){ return map.get(name); } } abstract class Coffee{ public abstract String getName(); public void addSugar(){ System.out.println("加糖"); } public void addMilk(){ System.out.println("加奶"); } } class AmericanCoffee extends Coffee{ @Override public String getName() { return "美式咖啡"; } } class LatteCoffee extends Coffee{ @Override public String getName() { return "拿铁咖啡"; } }
american=mode.AmericanCoffee latte=mode.LatteCoffee
解除了工厂和商品的耦合,当需要添加商品时,在配置文件中添加即可。
创建在静态代码块中的目的就是加载时只执行一次。不需要每次创建类时就创建。
总结
简单工厂解除业务和产品之间的耦合但有了产品和工厂的耦合,增加产品时不需要修改业务代码,但需要修改工厂类的代码。
工厂方法将一个具体工厂对应一个产品,用接口的多态替代了工厂中生产不同产品的逻辑判断,解除了工厂和产品的耦合,但容易出现类爆炸,同时工厂只能生成同一级别的产品(只有一个工厂接口的情况下)
抽象工厂将工厂的分类以产品族划分,一家工厂可以生成某个风格的系列产品,但增加新产品时,需要修改所有工厂的代码。
源码
工厂方法模式
编辑
产品抽象类 it = 工厂实现类.产品实现类
编辑
工厂方法和简单工厂是获取实现同一产品接口的类
抽象工厂可以获取实现不同接口的产品的类