你还在看错误的抽象工厂模式实现案例?
昨天在搜抽象工厂模式时,发现有好几篇博文讲的实现方式和我所认知的有出入,而且还看到某某教程讲的也是错误的还在搜索引擎排第一。
大家讲的简单工厂模式和工厂方法模式都是没问题的,关键到抽象工厂模式就不对了。
先从简单工厂模式和工厂方法模式复习一下,然后再看看错的是咋讲的。已经对抽象工厂模式烂熟于心的请忽略此文。
简单工厂模式:
有个一公共的产品接口,定义一些方法,各个具体的产品类都实现这个接口,然后有一个专门生产产品类实例的类被称作工厂类,专门为客户端生产产品实例,这个工厂的内部实现就是使用switch或ifelse进行逻辑判断实现的,根据客户端传递的参数不同,来决定什么时候创建什么样的具体产品,生产出不同的具体产品返回给客户端。这样,这个工厂类就集成了所有产品的创建逻辑,它变得无所不知,这也让它变成了核心。但这也成为了他的缺点,因为它将所有的逻辑集中放在一个类里,当产品接口有了新的产品实现类,工厂类需要增加代码,判断在什么时候创建该产品,就需要加入新的判断逻辑。
ShoesFactory工厂
public class ShoesFactory{ public static final String NIKE = "NIKE"; public static final String ADIDAS = "ADIDAS"; public static Shoes getShoes(String brand) { Shoes shoes = null; switch (brand) { case "NIKE": shoes = new NikeShoes(); break; case "ADIDAS": shoes = new AdidasShoes(); break; default: shoes = null; } return shoes; } }
测试类
public class SimpleFactoryTest{ public void main(String arg[]){ Shoes nikeShoes = ShoesFactory.getShoes(ShoesFactory.NIKE); nikeShoes.desc(); Shoes adidasShoes = ShoesFactory.getShoes(ShoesFactory.ADIDAS); adidasShoes.desc(); } }
缺点:每增加一个产品,都要新写一个类,然后在工厂类里增加分支,不符合java的开闭原则。
优点:对外屏蔽产品实例的创建,客户端只负责“消费”,通过这种方式实现了对职责的分割。
工厂方法模式:
有一个公共的产品接口和一个公共的产品工厂接口,任何具体工厂必须实现这个工厂接口。将具体的产品的实例创建任务交给具体的工厂类,工厂的职责单一化,每个产品实例从公共的工厂类内抽离出来,降低了耦合度。这样,当增加新的产品实例时,只需要增加相应的工厂类,符合开闭原则。
public interface ShoesFactory{ Shoes getShoes(); }
NikeShoesFactory
public class NikeShoesFactory implements ShoesFactory{ @Override public Shoes getShoes() { return new NikeShoes(); } }
AdidasShoesFactory
public class AdidasShoesFactory implements ShoesFactory{ @Override public Shoes getShoes() { return new AdidasShoes(); } }
当增加李宁鞋子时只需增加LiningShoes使其实现Shoes接口,然后增加LiningShoesFactory工厂类实现ShoesFactory接口使其负责生产LiningShoes的实例。
缺点:没增加一个具体产品都要增加相应的工厂类,增加代码量。
优点:符合开闭原则,易于扩展。
抽象工厂模式:
当有多个类型的产品时,产品类型之间横向发展,如增加裤子、上衣等产品。在工厂方法模式里,只有一个产品接口类鞋子,各种鞋子如耐克鞋、阿迪鞋实现鞋子接口,而工厂类只需要写2个就行了,耐克鞋工厂、阿迪鞋工厂类,各个工厂类职责单一只生产对应的一种具体产品。
而此时若增加了新的产品类型如裤子,他的具体产品阿迪裤子、耐克裤子,这个时候工厂类怎么实现呢?还像工厂方法类那样吗?如果业务上要求单一工厂原则(一个工厂只生产一个类型的产品),继续使用工厂方法没有问题,但如果需要提供一些列产品显然使用工厂方法模式是不行的。
此时随着多个产品类型的增加,产品之间形成一个个的产品族(耐克鞋、阿迪鞋是一个产品族,耐克裤子、阿迪裤子是一个产品族),而不同品牌之间形成产品等级。
网文实现方式
当我在网上搜索【抽象工厂模式】时,发现有不少文章给出的实现方式是如下图这样的。
抽象工厂模式结构:
代码实现
抽象工厂类
public abstract class AbstractFactory2{ public abstract Shoes getShows(String brand); public abstract Pants getPants(String brand); }
PantsFactory 裤子工厂
public class PantsFactory extends AbstractFactory2{ //裤子工厂不能生产鞋子,空实现 返回null @Override public Shoes getShows(String brand) { return null; } @Override public Pants getPants(String brand) { Pants pants = null; switch (brand) { case "NIKE": pants = new NikePants(); break; case "ADIDAS": pants = new AdidasPants(); break; default: pants = null; } return pants; } }
public class ShoesFactory extends AbstractFactory2{ @Override public Shoes getShows(String brand) { Shoes shoes = null; switch (brand) { case "NIKE": shoes = new NikeShoes(); break; case "ADIDAS": shoes = new AdidasShoes(); break; default: shoes = null; } return shoes; } //鞋子工厂不能生产鞋子,空实现 返回null @Override public Pants getPants(String brand) { return null; } }
FactoryProductor2工厂生产者,通过不同的产品名称创建具体工厂实例:(这里貌似简单工厂模式)
public class FactoryProductor2{ public static final String SHOES = "SHOES"; public static final String PANTS = "PANTS"; public static AbstractFactory2 getFactory(String productType) { if (SHOES.equalsIgnoreCase(productType)) { return new ShoesFactory(); } else if (PANTS.equalsIgnoreCase(productType)) { return new PantsFactory(); } return null; } }
测试类
public class AbstractFactoryTest2{ public static void main(String[] args) { AbstractFactory2 factory = FactoryProductor2.getFactory(FactoryProductor2.PANTS); Pants nikePants = factory.getPants("NIKE"); Pants adidasPants = factory.getPants("ADIDAS"); nikePants.desc(); adidasPants.desc(); factory = FactoryProductor2.getFactory(FactoryProductor2.SHOES); Shoes nikeShoes = factory.getShows("NIKE"); Shoes adidasShows = factory.getShows("ADIDAS"); nikeShoes.desc(); adidasShows.desc(); } }
NikePants
AdidasPants
NikeShoes
AdidasShoes
分析正确性
大家看这样的抽象工厂,每次创建产品都要知道产品的品牌,而且工厂类的另外一个方法被废弃。
还美名其曰的说产品族难扩展,产品等级易扩展。那我们就来看看在这种方式下是怎么个难易法。
产品族难扩展: 当我们想增加上衣Coat产品时,按现在的方式扩展一下看看。首先我们需要新建接口Coat,新建实现类NikeCoat和AdidasCoat实现Coat接口。然后我们需要在抽象工厂类增加创建Coat产品的方法,同时修改现有的2个工厂完成Coat相关实现类的实例创建逻辑,其次我们需要在FactoryProductor2增加分支逻辑创建CoatFactory。
撇开FactoryProductor2类不说,总结下我们都做了什么:
- 新增产品接口Cost
- 修改抽象工厂,新增getCoat方法,修改原有工厂增加getCoat的空实现
- 新增具体工厂CoatFactory
产品等级易扩展: 当我们想增加李宁品牌时,我们需要做什么?pants包下创建LiNingPants实现pants接口,shoes包下创建LiNingShoes实现Shoes接口,然后修改factory包下的ShoesFactory工厂和PantsFactory工厂,各自增加分支判断创建李宁牌的鞋子和裤子。
总结下我们都做了什么:
-
新增具体产品 LiNingPants 和 LiNingShoes
-
修改现有具体工厂类 ShoesFactory 工厂和 PantsFactory 工厂,增加创建李宁工厂分支
这样看来产品族扩展起来确实难,而且不符合开闭原则,但产品等级扩展起来却也不容易,也不符合开闭原则。
大家发现没有,现在的抽象工厂的具体工厂内部是怎么实现的?还是使用switch或ifesle分支,不论扩展产品族还是产品等级都需要改具体工厂类,增加分支,都不符合开闭原则。这是正确的抽象工厂??
NO!!
这样实现抽象工厂模式的不仅仅是搜到的几篇博文,而且还有*鸟教程,搜索排第一:)
正确的抽象工厂
看下这个图,发现具体工厂的名字变了,分别为AdidasFactory和NikeFactory,可想而知,以品牌命名具体的工厂只创建本品牌下的所有产品。
代码实现
抽象工厂类
public abstract class AbstractFactory{ public abstract Shoes getShows(); public abstract Pants getPants(); }
这样,每一个具体的工厂类,实现具体方法时,每个方法都能生成对应的产品,方法没有空实现。而且每一个具体工厂内部没有判断分支。
NikeFactory耐克工厂
public class NikeFactory extends AbstractFactory{ @Override public Shoes getShows() { return new NikeShoes(); } @Override public Pants getPants() { return new NikePants(); } }
AdidasFactory阿迪工厂
public class AdidasFactory extends AbstractFactory{ @Override public Shoes getShows() { return new AdidasShoes(); } @Override public Pants getPants() { return new AdidasPants(); } }
而对于工厂生产者来说,通过不同的品牌名称创建对应的工厂实例,
public class FactoryProductor{ public static final String NIKE = "NIKE"; public static final String ADIDAS = "ADIDAS"; public static AbstractFactory getFactory(String brand) { if (NIKE.equalsIgnoreCase(brand)) { return new NikeFactory(); } else if (ADIDAS.equalsIgnoreCase(brand)) { return new AdidasFactory(); } return null; } }
测试类
public class AbstractFactoryTest{ public static void main(String[] args) { AbstractFactory nikeFactory = FactoryProductor.getFactory(FactoryProductor.NIKE); Pants nikePants = nikeFactory.getPants(); Shoes nikeShows = nikeFactory.getShows(); nikePants.desc(); nikeShows.desc(); AbstractFactory adidasFactory = FactoryProductor.getFactory(FactoryProductor.ADIDAS); Shoes adidasShows = adidasFactory.getShows(); Pants adidasPants = adidasFactory.getPants(); adidasPants.desc(); adidasShows.desc(); } }
NikePants
NikeShoes
AdidasPants
AdidasShoes
分析
现在我们再来看一下,所谓的产品族难扩展,产品等级易扩展。
产品族难扩展: 当增加cost上衣产品时,首先需要新建包coat,新建接口Coat,新建实现类NikeCoat和AdidasCoat,然后在抽象工厂类新增抽象方法getCoat,ShoesFactory工厂和PantsFactory工厂分别实现该方法。
都做了什么:新建产品接口Coat,并创建实现类,抽象工厂新增抽象方法,原具体工厂类重写新增的抽象方法。
产品等级易扩展: 当增加新的品牌时,比如增加李宁牌的鞋子和裤子,怎么扩展?首先创建LiningShoes和LiningPants类分别实现Shoes和Pants接口,然后新建LiningFactory工厂类使其继承AbstractFactory抽象工厂类,重写getShoes和getPants方法。
都做了什么:新增产品实现类,新增具体工厂类。
类图
增加产品族
增加后,这个系统中有三个产品族,两个产品等级
增加产品等级
增加后,这个系统中2个产品族,3个产品等级
作者:walkinger
链接:https://juejin.im/post/5d4b8de1e51d453b1f37eac4