某天,你赚了钱,打算开一家自己的披萨店,披萨有若干种类,不过每种披萨的制作步骤都差不多,都要经过准备、烘烤、切片、装盒这几个步骤。非常自然,我们在设计中,会抽象出一个披萨的父类,父类中定义了披萨的制作步骤(prepare,bake,cut,box),不同的披萨有不同的准备过程,而烘烤、切片和装盒都一样操作,因此在父类中实现。具体的披萨类型继承父类,实现自己的准备过程。
好了,我们来实现一版。
披萨和他们的子类们
/**
* 披萨父类
*/
public abstract class Pizza {
protected String name;
//具体的子类披萨知道如何准备自己
public abstract void prepare();
public void bake(){
System.out.println(getName() + " 烘烤...");
}
public void cut(){
System.out.println(getName() + " 切片...");
}
public void box(){
System.out.println(getName() + " 装盒...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class CheesePizza extends Pizza{
public CheesePizza() {
setName("芝士披萨");
}
@Override
public void prepare() {
System.out.println("芝士披萨需要准备些芝士、芝麻、番茄、火腿,当然还有面团");
}
}
public class ClamPizza extends Pizza{
public ClamPizza() {
setName("蛤蜊披萨");
}
@Override
public void prepare() {
System.out.println("蛤蜊披萨,必须有蛤蜊啊");
}
}
public class PepperoniPizza extends Pizza{
public PepperoniPizza() {
setName("意式披萨");
}
@Override
public void prepare() {
System.out.println("意式披萨需要准备些芝麻、番茄、意大利火腿,橄榄油等,当然还有面团");
}
}
/**
* 蔬菜披萨
*/
public class VeggiePizza extends Pizza{
public VeggiePizza() {
setName("蔬菜披萨");
}
@Override
public void prepare() {
System.out.println("蔬菜披萨全TM是素的,吃毛线...");
}
}
很简单,不多讲。
现在我们实现一个披萨店,可以接受披萨订单,并创建不同种类的披萨。
我们看下披萨店的V1版本实现
/**
* V1版披萨店
*/
public class PizzaStoreV1 {
/**
* 下单
* @param type 披萨类型
* @return
*/
public Pizza orderPizza(String type){
Pizza pizza;
if("cheese".equals(type)){
pizza = new CheesePizza();
}else if("pepperoni".equals(type)){
pizza = new PepperoniPizza();
}else if("clam".equals(type)){
pizza = new ClamPizza();
}else if("veggie".equals(type)){
pizza = new VeggiePizza();
}else{
pizza = new CheesePizza();//不知道类型时,默认芝士披萨
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
测试下
/**
* V1版测试
*/
public class PizzaV1Main {
public static void main(String[] args) {
PizzaStoreV1 pizzaStore = new PizzaStoreV1();
//下单一个
pizzaStore.orderPizza("clam");
pizzaStore.orderPizza("veggie");
}
}
V1版的披萨店也没什么特殊的,下单方法中,传入披萨的类型,创建对应类型的披萨,按照披萨制作步骤制作,最后返回。
在经营过程中,由于某些原因,你需要将菜单中加入一些新的披萨类型,也会将一些卖的不好的类型从菜单中拿掉。对于这样的修改,披萨类型容易,我们只要扩展一种新的披萨子类即可,符合开闭原则。但是在下单方法中,
通过使用if-else方式,和具体的披萨子类是强耦合的,也就说,如果要添加或者移除披萨类型,需要修orderPizza方法中的if-else块,违反开闭原则,另外,我们之前学到,要将变化和固定的区分开来,将变化的部分封装起来,也就是封装变化。
在我们的orderPizza方法中,哪些是变化的部分,哪些是固定的部分呢?
现在,通过识别和封装变化,我们来实现V2版的披萨店看看效果吧。
/**
* V2版披萨店
*/
public class PizzaStoreV2 {
PizzaFactoryV2 pizzaFactory;
public PizzaStoreV2(PizzaFactoryV2 pizzaFactory) {
this.pizzaFactory = pizzaFactory;
}
/**
* 下单
* @param type 披萨类型
* @return
*/
public Pizza orderPizza(String type){
Pizza pizza = pizzaFactory.createPizzaByType(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
/**
* 披萨工厂(简单工厂)
*/
public class PizzaFactoryV2 {
public Pizza createPizzaByType(String type){
Pizza pizza;
if("cheese".equals(type)){
pizza = new CheesePizza();
}else if("pepperoni".equals(type)){
pizza = new PepperoniPizza();
}else if("clam".equals(type)){
pizza = new ClamPizza();
}else if("veggie".equals(type)){
pizza = new VeggiePizza();
}else{
pizza = new CheesePizza();//不知道类型时,默认芝士披萨
}
return pizza;
}
}
咦,创建披萨的代码(变化的部分)移到一个单独的方法中,这个方法专门用于创建披萨。这样PizzaStoreV2在增加和移除类型时,是不用变化的。
你可能会说,增加或者移除类型时,PizzaFactoryV2这个类要修改啊,一样不符合开闭原则的。
嗯,你说的对,上面这种方式称之为简单工厂模式(如果创建方法定义为静态的,就叫静态工厂),实际开发中,相信大家不知觉的都会使用这种模式,简单实用有效。
不过吧,不符合开闭原则也是它的缺点。
说到这里,是不是一个系统的所有变化都要满足开闭原则呢?
答案是否,而且通常你也做不到。一般来说,我们没有必要将系统的每个部分都设计成满足开闭原则的(就算做到了,也可能是一种浪费)你需要把注意力集中到最后可能变化的部分,然后设计它满足开闭原则。
上面这个简单工厂很好理解,我们继续。现在披萨店生意不错,名气也越来越好了,很多外地人也想享受这美味的披萨,是时候开启加盟模式了。
那么,现在我们需要在纽约和芝加哥开分店,而且纽约制作的是纽约风味的披萨,芝加哥制作的是芝加哥风味的披萨。为了便于后面理解,我们先把简单的产品(也就是披萨)建立起来。
纽约披萨风味
public class NYCheesePizza extends Pizza{
public NYCheesePizza() {
setName("纽约味芝士披萨");
}
@Override
public void prepare() {
System.out.println("芝士披萨需要准备些芝士、芝麻、番茄、火腿,当然还有面团");
}
}
public class NYClamPizza extends Pizza{
public NYClamPizza() {
setName("纽约味蛤蜊披萨");
}
@Override
public void prepare() {
System.out.println("蛤蜊披萨,必须有蛤蜊啊");
}
}
芝加哥披萨风味
public class ChicagoCheesePizza extends Pizza{
public ChicagoCheesePizza() {
setName("芝加哥味芝士披萨");
}
@Override
public void prepare() {
System.out.println("芝士披萨需要准备些芝士、芝麻、番茄、火腿,当然还有面团");
}
}
public class ChicagoClamPizza extends Pizza{
public ChicagoClamPizza() {
setName("纽约味蛤蜊披萨");
}
@Override
public void prepare() {
System.out.println("蛤蜊披萨,必须有蛤蜊啊");
}
}
现在,我们如何将不同口味的披萨和我们的披萨店配合起来,首先我们可以按照简单工厂的做法,用两个简单工厂来实现。披萨店类依赖工厂类,为了不依赖具体的工厂,我们抽象出来一个工厂接口,这就是不依赖具体,也就是依赖倒置原则的体现。
抽象工厂接口
/**
* 披萨工厂抽象
*/
public interface PizzaFactoryV3 {
Pizza createPizzaByType(String type);
}
芝加哥工厂(生产芝加哥风味披萨)和纽约工厂(生成纽约风味)实现,他们都是简单工厂
/**
* 芝加哥的披萨工厂(简单工厂)
*/
public class ChicagoPizzaFactoryV3 implements PizzaFactoryV3{
@Override
public Pizza createPizzaByType(String type){
Pizza pizza;
if("cheese".equals(type)){
pizza = new ChicagoCheesePizza();
}else if("clam".equals(type)){
pizza = new ChicagoClamPizza();
}else{
pizza = new ChicagoCheesePizza();//不知道类型时,默认芝士披萨
}
return pizza;
}
}
/**
* 纽约的披萨工厂(简单工厂)
*/
public class NYPizzaFactoryV3 implements PizzaFactoryV3 {
@Override
public Pizza createPizzaByType(String type){
Pizza pizza;
if("cheese".equals(type)){
pizza = new NYCheesePizza();
}else if("clam".equals(type)){
pizza = new NYClamPizza();
}else{
pizza = new NYCheesePizza();
}
return pizza;
}
}
披萨店实现
/**
* V3版披萨店
*/
public class PizzaStoreV3 {
PizzaFactoryV3 pizzaFactory;
public PizzaStoreV3(PizzaFactoryV3 pizzaFactory) {
this.pizzaFactory = pizzaFactory;
}
/**
* 下单
* @param type 披萨类型
* @return
*/
public Pizza orderPizza(String type){
Pizza pizza = pizzaFactory.createPizzaByType(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
可以看到披萨点和V2中的差不多,现在只是依赖工厂接口而已。
看看怎么使用
/**
* V3版测试
*/
public class PizzaV3Main {
public static void main(String[] args) {
//纽约的店
PizzaStoreV3 nyPizzaStore = new PizzaStoreV3(new NYPizzaFactoryV3());
//下单
nyPizzaStore.orderPizza("clam");
nyPizzaStore.orderPizza("cheese");
//芝加哥的店
PizzaStoreV3 chicagoPizzaStore = new PizzaStoreV3(new ChicagoPizzaFactoryV3());
chicagoPizzaStore.orderPizza("clam");
chicagoPizzaStore.orderPizza("cheese");
}
}
done,完美,如果要扩展新的披萨店,只需要实现一个新的工厂给到PizzaStoreV3就可以了。
同样的,我们可以换个思路,我们仔细观察V3版本中,我们是通过扩展工厂的方式来扩展,另一种方式是,我们可以抽象一个PizzaStore的基类,通过扩展其子类来达到开分店的目的,比如NYPizzaStore, ChicagoPizzaStore,然后将创建披萨的代码移到子类中。
实现一波
首先把产品(也就是披萨)搞定,这里我们可以直接复用V3版本的披萨。
然后是抽象的披萨店 PizzaStoreV4
/**
* V4版披萨店
*/
public abstract class PizzaStoreV4 {
/**
* 下单
* @param type 披萨类型
* @return
*/
public Pizza orderPizza(String type){
Pizza pizza = createPizzaByType(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizzaByType(String type);
}
披萨分店
/**
* 纽约分店
*/
public class NYPizzaStoreV4 extends PizzaStoreV4 {
@Override
protected Pizza createPizzaByType(String type) {
Pizza pizza;
if("cheese".equals(type)){
pizza = new NYCheesePizzaV4();
}else if("clam".equals(type)){
pizza = new NYClamPizzaV4();
}else{
pizza = new NYCheesePizzaV4();
}
return pizza;
}
}
/**
* 芝加哥分店
*/
public class ChicagoPizzaStoreV4 extends PizzaStoreV4 {
@Override
protected Pizza createPizzaByType(String type) {
Pizza pizza;
if("cheese".equals(type)){
pizza = new ChicagoCheesePizzaV4();
}else if("clam".equals(type)){
pizza = new ChicagoClamPizzaV4();
}else{
pizza = new ChicagoCheesePizzaV4();
}
return pizza;
}
}
最后我们来订购披萨吧
/**
* V4版测试
*/
public class PizzaV4Main {
public static void main(String[] args) {
//纽约的店
PizzaStoreV4 nyPizzaStore = new NYPizzaStoreV4();
//下单
nyPizzaStore.orderPizza("clam");
nyPizzaStore.orderPizza("cheese");
//芝加哥的店
PizzaStoreV4 chicagoPizzaStore = new ChicagoPizzaStoreV4();
chicagoPizzaStore.orderPizza("clam");
chicagoPizzaStore.orderPizza("cheese");
}
}
这种思路将创建披萨的任务放到子类中(披萨分店)去完成,在抽象的父类中(披萨店类)直接使用子类创建好的产品,而不管具体的创建细节。
诶,现在再要开分店,就可以扩展披萨店这个的子类来完成,也是可以满足开闭原则的。
V4的这种玩法(招式),是一种新的模式,叫工厂方法。简单的说,就是在父类中定义一个工厂方法(创建产品的方法,通常是抽象的),然后获得创建的产品直接使用。不同的子类可以实现(或者重写)这个方法,来生产不同种类的产品。
事实上也可以把V3版本的工厂抽象和工厂实现也可以叫工厂方法,只不过是工厂方法的一种特殊表现形式。
工厂方法的正式定义,工厂方法定义了一个创建的对象的接口,但有子类决定要创建哪个对象。工厂方法将类的实例化推迟到子类。
按照惯例,用代码来实现一波定义。
首先,需要有待创建的产品吧,先把产品写好。
/**
* 要被创建的产品抽象
*/
public interface Product {
void showMe();
}
/**
* 具体的产品A
*/
public class ConcreteProductA implements Product {
@Override
public void showMe() {
System.out.println("具体产品A");
}
}
/**
* 具体的产品B
*/
public class ConcreteProductB implements Product {
@Override
public void showMe() {
System.out.println("具体产品B");
}
}
然后,我们定义一个创建者父类,它会定义抽象的创建产品方法(这个方法就是工厂方法啦)。
/**
* 抽象的创建者
*/
public abstract class Creator {
/**
* 这个方法就是工厂方法
* @return
*/
protected abstract Product createProduct();
/**
* 其他方法可以使用工厂方法获取产品对象,而不用关心产品对象是怎么创建出来的。
*/
public void showProduct(){
Product product = createProduct();
product.showMe();
}
}
然后,我们创建两个不同的子类,分别来决定要创建的产品是哪一个。
/**
* 具体创建者A
*/
public class ConcreteCreatorA extends Creator{
@Override
protected Product createProduct() {
return new ConcreteProductA();
}
}
/**
* 具体创建者B
*/
public class ConcreteCreatorB extends Creator{
@Override
protected Product createProduct() {
return new ConcreteProductB();
}
}
最后,测试看看效果。
/**
* 工厂方法模式测试类
*/
public class FactoryMethodMain {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
creatorA.showProduct();
Creator creatorB = new ConcreteCreatorB();
creatorB.showProduct();
}
}
现在,我们要扩展新的产品时,只需要扩展产品子类,以及创建者子类即可,不修改原来的代码,符合开闭原则。
还没完,我们继续往下看看抽象工厂模式。有点长,但原理简单。
披萨店成功的关键在于新鲜、高质量的原料,如果加盟分店使用低价原料来增加利润,会毁了披萨店的品牌。
因此我们需要保持所有披萨店的原料一致。
上面的代码中,我们的披萨非常简单,为了后面更清晰的表达,先将披萨丰满起来,加入原料。
/**
* 披萨父类
*/
public abstract class PizzaV5 {
protected String name;
protected Dough dough;
protected Cheese cheese;
protected Clam clam;
//具体的子类披萨知道如何准备自己
public abstract void prepare();
public void bake(){
System.out.println(getName() + " 烘烤...");
}
public void cut(){
System.out.println(getName() + " 切片...");
}
public void box(){
System.out.println(getName() + " 装盒...");
}
/**
* 显示披萨原料
*/
public void showMaterial(){
System.out.println("显示披萨原料:");
dough.display();
clam.display();
cheese.display();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
其中3种原料,简单的由接口和两组实现组成
/**
* 面团
*/
public interface Dough {
void display();
}
/**
* 蛤蜊
*/
public interface Clam {
void display();
}
/**
* 奶酪
*/
public interface Cheese {
void display();
}
/**
* 纽约版本的面团
*/
public class NYDough implements Dough {
@Override
public void display() {
System.out.println("纽约版本的面团");
}
}
/**
* 纽约版本的蛤蜊(新鲜的蛤蜊)
*/
public class NYClam implements Clam {
@Override
public void display() {
System.out.println("纽约版本的蛤蜊");
}
}
/**
* 纽约版本奶酪
*/
public class NYCheese implements Cheese {
@Override
public void display() {
System.out.println("纽约版本奶酪");
}
}
/**
* 芝加哥版本的面团
*/
public class ChicagoDough implements Dough {
@Override
public void display() {
System.out.println("芝加哥版本的面团");
}
}
/**
* 芝加哥版本的蛤蜊
*/
public class ChicagoClam implements Clam {
@Override
public void display() {
System.out.println("芝加哥版本的蛤蜊");
}
}
/**
* 芝加哥版本奶酪
*/
public class ChicagoCheese implements Cheese {
@Override
public void display() {
System.out.println("芝加哥版本奶酪");
}
}
原料有了之后,披萨店的不同分店使用一组不同的原料版本,纽约的分店使用纽约版本的一组原料、芝加哥分店使用芝加哥分店的一组原料。
披萨店的父类和纽约分店、芝加哥分店和V4版本一样,使用的工厂方法模式来创建具体的披萨。
/**
* V5版披萨店
*/
public abstract class PizzaStoreV5 {
/**
* 下单
* @param type 披萨类型
* @return
*/
public PizzaV5 orderPizza(String type){
PizzaV5 pizza = createPizzaByType(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
pizza.showMaterial();
return pizza;
}
protected abstract PizzaV5 createPizzaByType(String type);
}
具体分店代码
/**
* 纽约分店
*/
public class NYPizzaStoreV5 extends PizzaStoreV5 {
@Override
protected PizzaV5 createPizzaByType(String type) {
PizzaV5 pizza;
MaterialFactory materialFactory = new NYMaterialFactory();
if("cheese".equals(type)){
pizza = new NYCheesePizzaV5(materialFactory);
}else if("clam".equals(type)){
pizza = new NYClamPizzaV5(materialFactory);
}else{
pizza = new NYCheesePizzaV5(materialFactory);
}
return pizza;
}
}
/**
* 芝加哥分店
*/
public class ChicagoPizzaStoreV5 extends PizzaStoreV5 {
@Override
protected PizzaV5 createPizzaByType(String type) {
PizzaV5 pizza;
MaterialFactory materialFactory = new ChicagoMaterialFactory();
if("cheese".equals(type)){
pizza = new ChicagoCheesePizzaV5(materialFactory);
}else if("clam".equals(type)){
pizza = new ChicagoClamPizzaV5(materialFactory);
}else{
pizza = new ChicagoCheesePizzaV5(materialFactory);
}
return pizza;
}
}
在具体创建披萨的代码中,传入了一个新的工厂类MaterialFactory,这个和我们之前使用的工厂有些区别。
在披萨父类中,我们发现要创建一个披萨出来,需要准备3种原料,面团、奶酪、蛤蜊,如果在具体的披萨中使用new来处理,这又回到了原始的耦合上去了,即具体的披萨耦合了具体的原料,如果某一种披萨要修改配方时,这种披萨的实现就会被修改,不符合开闭原则。
为了解决这个问题,我们继续引入新的工厂(专门用来创建对象的代码)来创建原料,因为每个披萨的创建,必然会创建依赖的3中原料,这3中原料的关系我们称之为产品家族,也就是相关联的一组产品。同工厂方法一样,首先定义创建原料的抽象方法(这儿使用接口来承载)
/**
* 原料工厂接口
*/
public interface MaterialFactory {
Dough createDough();
Cheese createCheese();
Clam createClam();
}
接下来,纽约的分店使用一组纽约版本的原料,那么实现一个纽约版本的原料工厂
/**
* 纽约的原料工厂
*/
public class NYMaterialFactory implements MaterialFactory {
@Override
public Dough createDough() {
return new NYDough();
}
@Override
public Cheese createCheese() {
return new NYCheese();
}
@Override
public Clam createClam() {
return new NYClam();
}
}
同样, 芝加哥的分店使用一组芝加哥版本的原料,那么实现一个芝加哥版本的原料工厂
/**
* 芝加哥的原料工厂
*/
public class ChicagoMaterialFactory implements MaterialFactory {
@Override
public Dough createDough() {
return new ChicagoDough();
}
@Override
public Cheese createCheese() {
return new ChicagoCheese();
}
@Override
public Clam createClam() {
return new ChicagoClam();
}
}
好了,我们在实现具体的披萨时,就可以使用不同的原料工厂来创建原料,具体披萨与工厂接口交互,不与具体的原料类交互,从而解耦。
看下4种披萨的实现
/**
* 纽约味蛤蜊披萨
*/
public class NYClamPizzaV5 extends PizzaV5{
MaterialFactory materialFactory;
public NYClamPizzaV5(MaterialFactory materialFactory) {
setName("纽约味蛤蜊披萨");
this.materialFactory = materialFactory;
}
@Override
public void prepare() {
System.out.println("纽约味蛤蜊披萨,必须有蛤蜊啊");
dough = materialFactory.createDough();
cheese = materialFactory.createCheese();
clam = materialFactory.createClam();
}
}
/**
* 纽约味芝士披萨
*/
public class NYCheesePizzaV5 extends PizzaV5{
MaterialFactory materialFactory;
public NYCheesePizzaV5(MaterialFactory materialFactory) {
setName("纽约味芝士披萨");
this.materialFactory = materialFactory;
}
@Override
public void prepare() {
System.out.println("芝士披萨需要准备些芝士、芝麻、番茄、火腿,当然还有面团");
dough = materialFactory.createDough();
cheese = materialFactory.createCheese();
clam = materialFactory.createClam();
}
}
/**
* 芝加哥味蛤蜊披萨
*/
public class ChicagoClamPizzaV5 extends PizzaV5{
MaterialFactory materialFactory;
public ChicagoClamPizzaV5(MaterialFactory materialFactory) {
setName("芝加哥味蛤蜊披萨");
this.materialFactory = materialFactory;
}
@Override
public void prepare() {
System.out.println("芝加哥味蛤蜊披萨,必须有蛤蜊啊");
dough = materialFactory.createDough();
cheese = materialFactory.createCheese();
clam = materialFactory.createClam();
}
}
/**
* 芝加哥味芝士披萨
*/
public class ChicagoCheesePizzaV5 extends PizzaV5{
MaterialFactory materialFactory;
public ChicagoCheesePizzaV5(MaterialFactory materialFactory) {
setName("芝加哥味芝士披萨");
this.materialFactory = materialFactory;
}
@Override
public void prepare() {
System.out.println("芝士披萨需要准备些芝士、芝麻、番茄、火腿,当然还有面团");
dough = materialFactory.createDough();
cheese = materialFactory.createCheese();
clam = materialFactory.createClam();
}
}
这种通过一个抽象的父类(接口和抽象类),定义产品族(相关联的一组产品)的创建接口,并提供不同版本的产品族创建实现,客户直接使用抽象的接口来获取一组创建好的对象,而不需要和具体的产品类耦合。这就是抽象工厂模式。
观察下V5版本的UML关系图
工厂方法解决了创建单个产品的问题(一个抽象方法来创建产品),抽象工厂解决了创建产品族的问题,即多个抽象的方法来创建相关的不同产品。
还是通过代码来深入理解下抽象工厂。
首先,定义下产品族
/**
* 产品家族成员A
*/
public interface ProductMemberA {
void showA();
}
/**
* 产品A的具体实现A1
*/
public class ConcreteProductA1 implements ProductMemberA {
@Override
public void showA() {
System.out.println("我是A1");
}
}
/**
* 产品A的具体实现A2
*/
public class ConcreteProductA2 implements ProductMemberA {
@Override
public void showA() {
System.out.println("我是A2");
}
}
/**
* 产品家族成员B
*/
public interface ProductMemberB {
void showB();
}
/**
* 产品B的具体实现B1
*/
public class ConcreteProductB1 implements ProductMemberB {
@Override
public void showB() {
System.out.println("我是B1");
}
}
/**
* 产品B的具体实现B2
*/
public class ConcreteProductB2 implements ProductMemberB {
@Override
public void showB() {
System.out.println("我是B2");
}
}
接下来,定义工厂接口,包括创建产品族的一组方法。
/**
* 产品家族工厂
*/
public interface ProductMemberFactory {
ProductMemberA createProductA();
ProductMemberB createProductB();
}
然后,提供不同版本的工厂实现
/**
* 具体工厂(系列1)
*/
public class ConcreteProductSeries1Factory implements ProductMemberFactory{
@Override
public ProductMemberA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductMemberB createProductB() {
return new ConcreteProductB1();
}
}
/**
* 具体工厂(系列1)
*/
public class ConcreteProductSeries2Factory implements ProductMemberFactory{
@Override
public ProductMemberA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductMemberB createProductB() {
return new ConcreteProductB2();
}
}
最后,RUN起来看看
/**
* 测试
*/
public class AbstractFactoryMain {
public static void main(String[] args) {
//系列1产品
ProductMemberFactory factory = new ConcreteProductSeries1Factory();
factory.createProductA().showA();
factory.createProductB().showB();
System.out.println();
//系列2产品
factory = new ConcreteProductSeries2Factory();
factory.createProductA().showA();
factory.createProductB().showB();
}
}
输出结果
我是A1
我是B1
我是A2
我是B2
最后,请大家思考下,抽象工厂在什么情境下满足开闭原则,在什么情景下不满足。
另外扩展一下,在V5版本的代码中,具体披萨代码中有大量的重复,你有什么方法来消除重复吗,会不会用到一种新的模式?
https://gitee.com/cq-laozhou/design-pattern