工厂模式是分为三种, 分别是简单工厂, 工厂方法, 抽象工厂. 其中工厂方法和抽象工厂是 GoF23 种设计模式中的一种, 而简单工厂则不是一种设计模式, 更加可以理解的是一种编码时候预定俗称的一种习惯. 那么, 就在接下来三点中分别去分析理解工厂模式.
一 简单工厂: 通过实例化一个工厂类, 来获取对应的产品实例. 我们不需要关注产品本身如何被创建的细节, 只需要通过相应的工厂就可以获得相应的实例. 简单工厂包括三种角色:
1. 工厂: 简单工厂模式的核心, 它负责实现创建所有实例的内部逻辑. 工厂类的创建产品类的方法可以被外界直接调用, 创建所需的产品对象.
2. 抽象产品 : 简单工厂模式所创建的所有对象的父类, 它负责描述所有实例所共有的公共接口.
3. 具体产品: 是简单工厂模式的创建目标, 所有创建的对象都是充当这个角色的某个具体类的实例.
比如以下例子:
(类设计的 UML 图)
1.Drinks 作为产品的抽象类并且有抽象方法 produce();(抽象产品)
- public abstract class Drinks {
- protected abstract void produce();
- }
2.Sprite 继承 Drinks 是要被具体生产出来的产品, 他重写了 produce()方法.(具体产品)
- public class Sprite extends Drinks {
- @Override
- protected void produce() {
- System.out.println("drink sprite");
- }
- }
3.Cola 同样也继承了 Drinks, 是要被生产出来的具体产品.
- (具体产品)
- public class Cola extends Drinks {
- @Override
- protected void produce() {
- System.out.println("Drink Cola");
- }
- }
4.DrinksFactory 为简单工厂, 向外暴露 produceDrink 方法来获取产品的实例(工厂)
- public class DrinksFactory {
- public Drinks produceDrink(Class className){
- try {
- return (Drinks) Class.forName(className.getName()).newInstance();
- } catch (InstantiationException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
5.Client 为应用层, Client 端想要获取到 Cola 或者 Sprite 对象, 只要通过 DrinkFactory 中的 produceDrink 方法传入相对应的对应的产品
- public class Client {
- public static void main(String[] args) {
- DrinksFactory factory = new DrinksFactory();
- Cola cola = (Cola) factory.produceDrink(Cola.class);
- cola.produce();
- }
- }
简单工厂的优点:
1. 不需要关心类的创建细节.
2. 减轻类之间的耦合依赖, 具体类的实现只是依赖于简单工厂, 而不依赖其他类.
简单工厂的缺点:
1. 扩展复杂, 当简单工厂需要生产出另外一种产品的时候, 需要扩展工厂的内部创建逻辑, 比较有可能引起较大的故障
2. 由于工厂类集中了所有实例的创建逻辑, 违反了高内聚责任分配原则, 将全部创建逻辑集中到了一个工厂类中
二. 工厂方法
工厂方法的定义是: 定义一个创建对象的接口, 让实现这个接口的的类去决定实例化具体的类. 工厂方法让类的实例化推迟到实现接口的子类中进行.
比如说, 我现在需要一瓶可乐, 有可口可乐公司生产的可乐也有百事可乐公司生产的可乐, 那么对于可乐这个产品等级(抽象工厂中会具体说明), 具体生产什么可乐并不是在一个工厂实现, 而是由一个可乐工厂指定一个标准(接口里面的抽象方法), 可口可乐公司百事可乐公司只要按照这个标准去生产就可以了.
(2)工厂方法的 Uml 类图
1.Cola 此类是产品的父类
- public abstract class Cola {
- protected abstract void drinks();
- }
2.PepsiCola 继承 Cola, 是要生产的产品之一
- public class PepsiCola extends Cola {
- @Override
- protected void drinks() {
- System.out.println("Drinks PepsiCola");
- }
- }
3.CoCoCola 同样继承 Cola, 也是要生产的产品之一
- public class CoCoCola extends Cola {
- @Override
- protected void drinks() {
- System.out.println("Drinks cococoLa");
- }
- }
4.ColaFacotry 定义抽象工厂, 指定要生产此类产品的规范(存在的方法与属性), 指定工厂方法
- public interface ColaFacotry {
- Cola produce(Class<Cola> cola);
- }
5.PepsiColaFactory 定义子类工厂, 它继承抽象工厂, 实现了对某一产品等级的产品的获得
- public class PepsiColaFactory implements ColaFacotry {
- public PepsiCola produce(Class cola) {
- if (cola.isInstance(PepsiCola.class)){
- return new PepsiCola();
- }
- return null;
- }
- }
6.ColaFacotry, 是规定工厂方法去获得拿一些的产品等级的商品, 比如说, 我规定生产的产品等级是可乐和草莓可乐, 那么对于它的实现类来说, 也就是其子类中的重载方法来说去具体实现获取产品的具体实现.
- public interface ColaFacotry {
- Cola produce(Class<Cola> cola);
- }
三. 抽象工厂
抽象工厂是提供了创建一系列服务的的对象的接口. 那么问题就来了, 怎么区分和工厂方法中的服务对象宁? 此时就需要对一组概念有所理解, 即产品等级和产品族, 我从网上找到下面这张图, 进行解释说明. 在图 (4) 中, 我们可以通过横向和纵向的比较, 横向是某一个手机厂商如苹果, 小米等, 他们不仅仅生产手机, 还生产电脑, 耳机等一系类产品, 那么我们把苹果, 小米, 华为这样的厂商可以认为他们生产的是一个产品族, 而他们自己本身就是一个抽象工厂的具体实现; 那么纵向来看, 不管是小米华为还是苹果, 他们生产的产品是按照一定的规则来生产, 显示屏, 电池, 处理器等等, 所以对于纵向的产品来说, 他们又是属于同一个产品等级, 我们亦可以称他们的实现为工厂方法.
综上所述, 抽象工厂解决的是横向的产品族, 工厂方法解决的是纵向的产品等级. 具体抽象工厂请看代码.
(3)产品等级和产品族(图片来自 https://laravel-china.org/topics/18598?order_by=created_at&)
(4)产品等级示意图
1.AbstractFactory 抽象工厂
- public interface AbstractFactory {
- public Phone producePhone();
- public Computer producaComputer();
- }
2.AppleFactory 具体工厂的实现一
- public class AppleFactory implements AbstractFactory {
- public Phone producePhone() {
- return new iPhone();
- }
- public Computer producaComputer() {
- return new Mac();
- }
- }
3.MiFactory 具体工厂的实现二
- public class MiFactory implements AbstractFactory {
- public Phone producePhone() {
- return new MiPhone();
- }
- public Computer producaComputer() {
- return new MiComputer();
- }
- }
4.Phone 抽象产品等级一
- public abstract class Phone {
- public abstract void call();
- }
5.iPhone 具体产品一
- public class iPhone extends Phone {
- @Override
- public void call() {
- System.out.println("Iphone call");
- }
- }
6.MiPhone 具体产品一
- public class MiPhone extends Phone {
- @Override
- public void call() {
- System.out.println("Mi Phone call");
- }
- }
7.Computer 抽象产品等级二
- public abstract class Computer {
- public abstract void work();
- }
8.Mac 具体产品一
- public class Mac extends Computer {
- @Override
- public void work() {
- System.out.println("MAC work");
- }
- }
9.MiComputer 具体产品二
- public class MiComputer extends Computer {
- @Override
- public void work() {
- System.out.println("MI computer word");
- }
- }
10. 客户端
- public class Clint {
- public static void main(String[] args) {
- AppleFactory appleFactory = new AppleFactory();
- appleFactory.producaComputer().work();
- appleFactory.producePhone().call();
- MiFactory miFactory = new MiFactory();
- miFactory.producaComputer().work();
- miFactory.producePhone().call();
- }
- }
(5)抽象工厂 UML 类图
从 UML 类图中不难看出, 我们如果需要拓展抽象工厂里面的方法会比较麻烦, 因为我们必须修改抽象类以及添加对应的产品等级, 这样修改量比较大, 但是每种产品之间相互解耦, 符合程序设计的 "高内聚低耦合" 的思想.
最后, 不管是抽象工厂还是工厂方法甚至是简单工厂, 他们的存在都有一定的优缺点, 在设计程序的时候要根据具体情况进行取舍, 不存在那种设计好那种设计特别差, 只是针对不同的业务场景的不一样的处理.
来源: http://www.bubuko.com/infodetail-2865368.html