工厂方法模式、简单工厂模式、抽象工厂模式
- 前言:平时创建对象时,通过new关键字,如User user = new User();,在一些情况下,要创建的对象需要经过一系列复杂的初始化操作,比如查配置文件、查数据库、初始化成员对象等,如果都放在构造函数,极大的影响性能。这时候一个专门负责对象的创建的类就是必要的,这就是工厂类,这种做法就是工厂模式,在任何需要生成复杂对象的地方,都可以使用。包括简单工厂(不在23中设计模式中)、工厂方法和抽象工厂。
解决的问题
- 客户端再调用时不想判断实例化哪一个类,或者实例化的过程过于复杂。
再工厂模式中,具体的实现类创建过程中对客户端是透明的,客户端不决定具体实例化哪一个类,而是交由”工厂“来实例化。
简单工厂模式
- 结构:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类。
- 抽象类或接口:定义了要创建的产品对象的接口
- 具体方法:具有统一父类的具体类型的产品
- 产品工厂:负责创建产品对象。工厂模式同样体现了开闭原则,将”创建具体产品实现类“这部分变化的代码从不变化的代码”使用产品“中分离出来,之后想要新增产品时,只需要扩展工厂的实现类即可。
//创建不同品牌的键盘 //抽象接口 publci interface Keyboard{ void print(); void input(Context context); } //具体实现 class HPkeyboard implements Keyboard{ @Override public void print(){ } @Override public void input(Context context){ } } //具体实现 class DellKeyboard implements Keyboard{ @Override public void print(){ } @Override public void input(Context context){ } } //具体实现 class LenovoKeyboard implements Keyboard{ @Override public void print(){ } @Override public void input(Context context){ } } //产品工厂 public class KeyboardFactory{ public Keyboard getInstance(int brand){ if(BrandEnum.HP.getCode() == brand){ return new HPKeyboard(); }else if(BrandEnum.LENOVO.getCode() == brand){ return new LenovoKeyboard(); }else if(BrandEnum.DELL.getCode() == brand){ return new DellKeyboard(); } return null; } public static void main(String[] args){ KeyboardFactory keyboardFactory = new KeyboardFactory(); keyboard lenovoKeyboard = KeyboardFactory.getInstance(BrandEnum.LENOVO.getCode()); } }
- 上边的工厂实现是一个具体的类KeyboardFactory,而非接口或者抽象类,getInstance()方法利用if-else创建并返回具体的键盘实例,如果增加新的键盘子类,键盘工厂的创建方法中就要增加新的if-else。这种做法扩展性差,违背了开闭原则,也影响了可读性。所以,这种方式使用在业务较简单,工厂类不会经常更改的情况。
工厂方法模式
- 为了解决上面提到的”增加if-esle“的问题,可以为每一个键盘子类建立一个对应的工厂子类,这些工厂子类实现同一个抽象工厂接口。这样,创建不同品牌的键盘,只需要实现不同的工厂子类。当有新品牌加入时,新建具体工厂继承抽象工厂,而不用修改任何一个类。
- 结构
- 抽象工厂:声明了工厂方法的接口。
- 具体产品工厂:实现工厂方法的接口,负责创建产品对象。
- 产品抽象类或接口:定义工厂方法所创建
//抽象工厂 public interface IKeyboardFactory{ Keyboard getInstance(); } //具体产品工厂 public class HPKeyboardFactory implements IKeyboardFactory{ @Override public Keyboard getInstance(){ return new HPKeyboard(); } } //具体产品工厂 public class LenovoFactory implements IKeyboardFactory{ @Override public Keyboard getInstance(){ return new lenovoKeyboard(); } } //具体产品工厂 public class DellFactory implements IKeyboardFactory{ @Override public Keyboard getInstance(){ return new DellKeyboard(); } }
- 缺点:每一种品牌对应一个工厂类,在创建具体键盘对象时,实例化不同的工厂类。但是,如业务涉及的子类越来越多,子类对应的工厂类也多,这样会使得系统中类的个数成倍增加,增加了代码的复杂的。
抽象工厂模式
- 为了缩减工厂实现子类的数量,不必给每一个产品分配一个工厂类,可以将产品进行分组,每组中的不同产品由同一个工厂类的不同方法来创建。例如,键盘。主机这两种产品可以分配到同一个分组—电脑,而不同品牌的电脑由不同的制造商工厂来创建。
把产品类分组,组内不同产品由同一工厂类的不同方法实现的设计模式,就是抽象工厂模式。
抽象工厂适用于:
- 一个系统要独立于它的产品的创建、组合和表示时;
- 一个系统要由多个产品系列中的一个来配置时;
- 要强调一系列相关的产品对象的设计以便进行联合使用时;
- 当你提供一个产品类库,而只想显示他们的接口而不是实现时;
结构
- 抽象工厂:声明了创建抽象产品对象的操作接口。
- 具体产品工厂:实现了抽象工厂的接口,负责创建产品对象。
- 产品抽象类或接口:定义一类产品对象的接口。
- 具体产品实现:定义一个将被相应具体工厂创建的产品对象。
//抽象工厂 public interface Keyboard{ void printf(); } //具体产品工厂,抽象的实现 public class DellKeyboard implements Keyboard{ @Override public void print(){ //TODO } } //具体产品工厂 public class HPKeyboard implements Keyboard{ @Override public void printf(){ //TODO } } //抽象工厂 public interface Monitor{ void play(); } //具体产品工厂 public class DellMonitor implements Monitor{ @Override public void play(){ //TODO } } //具体产品工厂 public class HPMonitor implements Monitor{ @Override public void play(){ //TODO } } //抽象工厂 public interface MainFrame{ void run(); } //具体产品工厂 public class DellMainFrame implements MainFrame{ @Override public void run(){ //TODO } } //具体产品工厂 public class HPMainFrame implements MainFrame{ @Override public void run(){ //TODO } } //工厂类。工厂分Dell工厂和HP工厂,各自负责品牌内产品的创建。 //产品抽象类或接口:定义一类产品对象的接口 public interface IFactory{ MainFrame createMainFrame(); //Mointor createMainFrame(); Mointor createMoitor(); Keyboard createkeyboard(); } //具体产品实现 public class DellFactory implements IFactory{ @Override public MainFrame createMainFrame(){ MainFrame mainFrame = new DellMainFrame(); //... return mainFrame } @Override public Mointor createMonitor(){ Monitor monitor = new DellMonitor(); //... return monitor; } @Override public Keyboard createKeyboard(){ Keyboard keyboard = new DellKeyboard(); //... return keyboard; } } //具体产品实现 public class HPFactory implements IFactory{ @Override public MainFrame createMainFrame(){ MainFrame mainFrame = new HPMainFrame(); //... return mainFrame } @Override public Mointor createMonitor(){ Monitor monitor = new HPMonitor(); //... return monitor; } @Override public Keyboard createKeyboard(){ Keyboard keyboard = new HPKeyboard(); //... return keyboard; } } //客户端代码,实例化不同的工厂子类,可通过不同的创建方法创建不同的产品 public class Main{ public static void main(String[] args){ IFactory dellFactory = new DellFactory(); IFactory HPFactory = new HPFactory(); //创建键盘 Keyboard dellKeyboard = dellFactory.createKeyboard(); } }
优缺点:增加分组很简单,分组中的产品扩展很困难,要增加一个鼠标Mouse,既要创建抽象接口Mouse,又要增加具体实现DellMouse,还要在每个Factory中定义创建鼠标的方法实现createMouse()。
总结:
- 简单工厂:唯一工厂类,一个产品抽象类,工厂类的创建方法依据入参判断并创建具体产品对象。
- 工厂方法:多个工厂类,一个产品抽象,利用多态创建不同的产品对象,避免了大量if-else判断。
- 抽象工厂:多个工厂类,多个产品抽象类,产品子类分组,同一个工厂实现类创建同组中的不同产品,减少了工厂子类的数量。
可以在一下情况考虑工厂模式:
- 在编码时不能预见需要创建哪种类型的实例。
- 系统不应依赖于产品类实例如何被创建、组合和表达的细节。
总之,工厂模式就是为了方便创建同一接口定义的具有复杂参数和初始化步骤的不同对象。工厂模式一般用来创建复杂对象。只需用new就可以创建成功的简单读一下,无需使用工厂模式,否则会增加系统的复杂度。
此外,若对象的参数是不固定,推荐使用Bulider模式。在实际项目中,结合Spring中的InitializingBean接口,可以利用@Autowried注解实现工厂。