一、概念
- 工厂方法模式:用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。这样,关于超类的代码和子类创建对象的代码之间就解耦了。
- 抽象工厂模式(Abstract Factory Pattern):提供一个接口,用来创建相关或依赖对象的家族,而不需要明确指定具体类。这样,产品创建的过程只会依赖于接口,而不关心具体的实现是什么,从而达到解耦的目的。
- 角色 - 工厂方法模式:
1、抽象工厂(Creator):定义了一个抽象的工厂方法,让子类实现此方法制造产品。
2、具体工厂(ConcreteCreator):实现抽象工厂方法,包含具体生产产品的实现代码,返回一个产品的实例。
3、抽象产品(Product):定义一个抽象的产品,为了工厂中创建产品类型的匹配。
4、具体产品(ConcreteProduct):实现抽象产品,包含各自产品的特色代码。 - 角色 - 抽象工厂模式:
1、抽象工厂(Creator):一般是接口或抽象类,定义了一系列的产品家族。
2、具体工厂(ConcreteCreator):实现抽象工厂方法,包含一系列产品家族的实现代码。
3、抽象产品(Product):定义一个抽象的产品,为了工厂中创建产品类型的匹配。
4、具体产品(ConcreteProduct):实现抽象产品,包含各自产品的特色代码。
二、Demo 实现 - 工厂方法模式
TOPIC:我们要定义一个披萨店,并允许个人或机构加盟,而且个人或机构可以根据当地不同的口味生产不同的披萨。
1、抽象工厂 - PizzaStore.java
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
System.out.println(pizza.name);
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);
}
这里,我们定义了一个抽象工厂角色,并定义了一个抽象工厂方法。这样产品(Pizza)的实例化过程会交由抽象工厂的子类 —— 具体工厂去创建。
2、抽象产品 - Pizza.java
public abstract class Pizza {
/**
* 披萨的名字
*/
protected String name;
protected void bake() {
System.out.println("烘烤25分钟...");
}
protected void cut() {
System.out.println("把匹萨沿着对角线切开...");
}
protected void box() {
System.out.println("包装用官方标准的盒子...");
}
}
这里,我们定义了一个抽象产品角色,之所以把产品定义成抽象的,有两个原因:一是为了在具体工厂中生产的产品可以用统一的抽象父类来接收;二是为了让加盟商可以任意的扩展自己的产品。这种设计符合我们的设计原则 —— 面向接口(或抽象)编程,不面向实现编程。
3、具体产品
现在有加盟商要加盟我们的商店,他希望能够按照他们的地方特色,自定义他们要出售的披萨...那么,只要扩展 Pizza 类就可以了!
public class CheesePizza extends Pizza {
public CheesePizza() {
name = "这是一个奶酪披萨";
}
@Override
protected void bake() {
System.out.println("烘烤30分钟...");
}
@Override
protected void cut() {
System.out.println("把匹萨按四等分切开...");
}
@Override
protected void box() {
System.out.println("包装用奶酪披萨特制的盒子...");
}
}
public class DurianPizza extends Pizza {
public DurianPizza() {
name = "这是一个榴莲披萨";
}
@Override
protected void bake() {
System.out.println("烘烤45分钟...");
}
@Override
protected void cut() {
System.out.println("把匹萨按三等分切开...");
}
@Override
protected void box() {
System.out.println("包装用榴莲披萨特制的盒子...");
}
}
4、具体工厂 - PizzaFactory.java
public class PizzaFactory extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
switch (type) {
case "cheese":
return new CheesePizza();
case "durian":
return new DurianPizza();
default:
break;
}
return null;
}
}
这里,我们定义了一个具体工厂角色,继承自抽象工厂,产品(Pizza)的具体实例化代码在这里实现。
5、测试
public class Test {
public static void main(String[] args) {
PizzaStore store = new PizzaFactory();
store.orderPizza("cheese");
store.orderPizza("durian");
}
}
三、使用抽象工厂模式重构代码
1、抽象工厂 - AbstractFactory.java
public interface AbstractFactory {
/**
* @Description 奶酪披萨制造接口
*/
Pizza createCheese();
/**
* @Description 榴莲披萨制造接口
*/
Pizza createDurian();
}
这里,我们定义了一个抽象工厂角色,我们用接口定义了一系列的产品家族。
2、具体工厂
public class AFactory implements AbstractFactory {
@Override
public Pizza createCheese() {
System.out.println("A工厂制造的奶酪披萨");
return new CheesePizza();
}
@Override
public Pizza createDurian() {
System.out.println("A工厂制造的榴莲披萨");
return new DurianPizza();
}
}
public class BFactory implements AbstractFactory {
@Override
public Pizza createCheese() {
System.out.println("B工厂制造的奶酪披萨");
return new CheesePizza();
}
@Override
public Pizza createDurian() {
System.out.println("B工厂制造的榴莲披萨");
return new DurianPizza();
}
}
这是两个具体的工厂类,实现了抽象工厂接口,包含了一系列产品家族的实现代码。
3、抽象产品和具体产品
抽象产品和具体产品的代码和工厂方法模式一致,这里就不加赘述了,来看看抽象工厂的使用吧!
4、测试
public class PizzaStore {
private AbstractFactory abstractFactory;
public PizzaStore(AbstractFactory abstractFactory) {
this.abstractFactory = abstractFactory;
}
public void prepare() {
this.abstractFactory.createCheese();
this.abstractFactory.createDurian();
}
}
public class Test {
public static void main(String[] args) {
AbstractFactory abstractFactory = new AFactory();
PizzaStore pizzaStore = new PizzaStore(abstractFactory);
pizzaStore.prepare();
AbstractFactory abstractFactory2 = new BFactory();
PizzaStore pizzaStore2 = new PizzaStore(abstractFactory2);
pizzaStore2.prepare();
}
}
抽象工厂模式在使用时,利用对象组合和多态将不同的工厂实现类注入到代码中。这样,代码只会依赖接口,根本不关心具体实现是什么,以此达到解耦的目的。
演示源代码:https://github.com/JMCuixy/design-patterns/tree/master/src/main/java/com/example/factory
四、总结
- 依赖倒置原则:要依赖抽象,不要依赖具体类 —— 不能让高层组件依赖底层组件,而且,不管是高层还是底层组件,都应该依赖于抽象。想要遵循依赖倒置原则,工厂模式并非是唯一的技巧,但却是最有威力的技巧之一。
- 编写模块的实现依赖于抽象,在运行时传入具体的实现细节, 这就是依赖倒置的工作原理。依赖倒置原则教我们尽量避免使用具体类,而多使用抽象。
- 依赖倒置原则的目的是让程序员脱离底层粘合代码,编写上层业务逻辑代码。这就让上层代码依赖于底层细节的抽象,从而可以重用上层代码。这种模块化和重用方式是双向的:既可以替换不同的细节重用上层代码,也可以替换不同的业务逻辑重用细节的实现。
- 优点:
1、封装变化。将创建对象的代码集中在一个对象或方法中,可以避免代码的重复。
2、面向接口编程。在实例化对象时,向客户隐藏了实例化的细节,用户只需要关心所需产品对应的工厂,无需关心创建细节,甚至无须知道具体产品类的类名。 - 缺点:
1、系统中类的个数会增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销。
2、由于考虑到系统的可扩展性,需要引入抽象层,增加了系统的抽象性和理解难度。 - 工厂方法模式和抽象工厂模式的区别?
1、工厂方法和抽象工厂的任务都是负责实例化对象,但是工厂方法用的方式是继承,而抽象工厂方式用的方法是对象组合。
2、工厂方法注重于:把对象实例化的代码从具体类中解耦出来 —— 通过子类继承实现,或者目前还不知道将来需要实例化哪些具体类时;抽象工厂注重于:当你需要创建产品家族和想让制造的相关产品集中起来时。