简述
学习设计模式最重要的原因是解耦,在现实社会中我们知道原始社会自给自足(没有工厂),慢慢的有了小作坊(简单工厂),而后有了工厂流水线(工厂方法),现在是产业链代工厂(抽象工厂),那么我们的代码也是由简而繁一步一步迭代而来,但对于调用者来说确是越来越简单化 。
简单工厂模式
简单工厂模式(Simple Factory Pattern)是指由一个工厂对象来决定创建哪一种产品类的对象,但他不属于GOF,23种设计模式。简单工厂适用于工厂类负责创建的产品类比较少的场景,调用端只需要传入参数,无需关心产品类内部创建对象的逻辑。
下面我们来看代码示例
我们以某工厂生产产品为例,我们可以先定义一个标准产品接口
public interface IProduct { void produce(); }
创建一个电视实现类
public class TVProduct implements IProduct { @Override public void produce() { System.out.println("生产电视"); } }
调用代码
public static void main(String[] args) { IProduct product = new TVProduct(); product.produce(); }
上面的代码,父类IProduct指向子类TVProduct 的引用,调用端需要依赖TVProduct类,如果业务需要增加FridgeProduct或者更多,调用端依赖就会变得越来越臃肿。因此我们需要减少这种依赖,把创建产品的细节隐藏,我们使用简单工厂来对代码进行优化,先创建FridgeProduct类
public class FridgeProduct implements IProduct { @Override public void produce() { System.out.println("生产冰箱"); } }
创建简单工厂类
public class ProductFactory { public IProduct createProduct(String productName) { if ("TV".equals(productName)) return new TVProduct(); else if("Fridge".equals(productName)) return new FridgeProduct(); return null; } }
测试类
public static void main(String[] args) { ProductFactory productFactory = new ProductFactory(); IProduct product= productFactory.createProduct("Fridge"); product.produce(); }
对应调用端无需知道对象创建细节,我们只需要告诉工厂类我们需要的类即可,下面来看下类图
我们发现客户端调用时简单了,但是当我们有新的产品扩展时,我们需要修改工厂类的produce方法,这样违背类开闭原则,因此我们可以继续优化代码,利用反射修改工厂类
public class ProductFactory { public IProduct createProduct(String className) { try { if (!(className == null || className.equals(""))) return (IProduct)Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } }
调用代码
public static void main(String[] args) { ProductFactory productFactory = new ProductFactory(); IProduct product = productFactory.createProduct("com.boke.pattern.factory.simplefactory.FridgeProduct"); product.produce(); }
现在无论产品怎样扩展丰富,我们的工厂类是不需要修改的。但是有个问题我们输入的参数是字符串,无法约束调用方,所以我们再优化一下工厂类
public class ProductFactory { public IProduct createProduct(Class<? extends IProduct> clazz) { try { if (clazz != null) return (IProduct)clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } }
调用端代码
public static void main(String[] args) { ProductFactory productFactory = new ProductFactory(); IProduct product= productFactory.createProduct(FridgeProduct.class); product.produce(); }
简单工厂在JDK源码也是有很多比如Calendar,可以查看Calendar.getInstance()方法
简单工厂的缺点:工厂类的职责相对过重,其实新增产品时还是需要修改工厂类逻辑,不易于扩展过于复杂的产品结构
工厂方法模式
工厂方法模式(Factory Method Pattern)是指定义一个创建对象的工厂接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例推迟到子类进行。工厂接口仅负责具体工厂子类必须实现的接口。抽象父工厂如ProductFactory,而电视和冰箱等及时在子工厂中实现。在工厂方法模式中用户只关心所需产品对应的工厂无需关心创建细节,而且当新增产品时,无需修改已经创建的工厂,符合开闭原则
工厂方法模式主要解决产品扩展问题,在简单工厂中随着产品链的丰富,如果每个产品的创建逻辑有区别,工厂的职责会越来越多,不便维护。根据单一职责原则我们将职责进行拆分专人干专事,TV由TV工厂创建,Fridge由Fridge工厂创建,工厂本身是个抽象,来看代码产品抽象工厂
IProductFactory接口:
public interface IProductFactory { IProduct createProduct(); }
分别创建子工厂类TVFactory
public class TVFactory implements IProductFactory { @Override public IProduct createProduct() { return new TVProduct(); } }
FridgeFactory类
public class FridgeFactory implements IProductFactory { @Override public IProduct createProduct() { return new FridgeProduct(); } }
测试代码
public class FactoryMethodTest { public static void main(String[] args) { IProductFactory factory = new TVFactory(); IProduct product = factory.createProduct(); product.produce(); factory = new FridgeFactory(); product = factory.createProduct(); product.produce(); } }
再来看下类图
工厂方法缺点:
- 如果产品很多那么类也就会多,增加复杂度
- 增加系统的抽象性和理解难度
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是指是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。强调的是一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码我们要了解两个概念产品等级结构和产品族,看下面的图:
产品等级结构:即产品抽象的实现结构,如一个抽象类是电视机,其子类有小米电视机、华为电视机、西门子电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类,图上假如椭圆代表电视机,不同颜色就是一个产品等级结构
产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如小米工厂生产的小米电视机、小米手机构成了一个产品族。
接下来我们以手机和电视机为例看一下代码示例
产品族手机与电视机抽象接口
public interface IPhone { void produce(); }
public interface ITV { void produce(); }
小米手机与电视机实现类
public class MIPhone implements IPhone { @Override public void produce() { System.out.println("小米手机生产..."); } }
public class MITV implements ITV { @Override public void produce() { System.out.println("小米电视生产..."); } }
华为手机与电视机实现类
public class HWPhone implements IPhone { @Override public void produce() { System.out.println("华为手机生产..."); } }
public class HWTV implements ITV { @Override public void produce() { System.out.println("华为电视生产..."); } }
抽象工厂接口
public interface IProductFactory { ITV createTV(); IPhone createPhone(); }
小米工厂实现类
public class MIProductFactory implements IProductFactory { @Override public ITV createTV() { return new MITV(); } @Override public IPhone createPhone() { return new MIPhone(); } }
华为工厂实现类
public class HWProductFactory implements IProductFactory { @Override public ITV createTV() { return new HWTV(); } @Override public IPhone createPhone() { return new HWPhone(); } }
测试类
public class AbstractFactoryTest { public static void main(String[] args) { IProductFactory productFactory = new HWProductFactory(); productFactory.createPhone().produce(); productFactory.createTV().produce(); productFactory = new MIProductFactory(); productFactory.createPhone().produce(); productFactory.createTV().produce(); } }
看下类图
上面的代码完整地描述了两个产品族 小米产品和华为产品,也描述了两个产品等级手机和电视机。抽象工厂非常完美清晰地描述这样一层复杂的关系。但是,不知道大家有没有发现,如果我们再继续扩展产品等级,将路由器加入到产品中,那么我们的代码从抽象工厂,到具体工厂要全部调整,很显然不符合开闭原则。因此抽象工厂也是有缺点的:
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
- 增加了系统的抽象性和理解难度。
但在实际应用中,我们千万不能觉得违背开闭原则就不使用。在实际需求中产品等级结构升级是非常正常的一件事情。我们可以根据实际情况,只要不是频繁升级,可以不遵循开闭原则。代码每半年升级一次或者每年升级一次又有何不可呢
参考资料:https://en.wikipedia.org/wiki/Design_Patterns#Patterns_by_Type