[设计模式]工厂模式
@TOC
手机用户请
横屏
获取最佳阅读体验,REFERENCES
中是本文参考的链接,如需要链接和更多资源,可以关注其他博客发布地址。
平台 | 地址 |
---|---|
CSDN | https://blog.csdn.net/sinat_28690417 |
简书 | https://www.jianshu.com/u/3032cc862300 |
个人博客 | https://yiyuery.github.io/NoteBooks/ |
工厂模式是个统一的泛称,具体可以分为:
- 简单工厂(静态工厂)
- 工厂方法模式
- 抽象工厂模式
其中,简单工厂是种编程的习惯,并不是种设计模式。静态工厂则是简单工厂的一种变体,即使用静态方法定义一个简单工厂,我们就称之为静态工厂.
工厂模式处理的是对象的创建的统一封装问题。
静态工厂
场景描述
背景:
一个工厂有个手机生产线,可以支持生产和组装不同品牌的手机,不同的代理商需要向工厂订购不同数量、不同类型的手机。
初始问题:
不同手机的代理商每次需要进不同品牌的手机。每次去找工厂的时候,都会 new 一个新的手机出来。那么M种品牌代理商的话,如果在工厂都下订单,工厂需要定义 M 个手机对象。如果某个品牌该代理商暂时不使用了,但是其他代理商还要使用,工厂就需要修改给代理商提供手机的类。
对于这个问题,我们用静态工厂来看下如何解决:
代码语言:javascript复制//抽象手机对象
@Data
public abstract class AbstractPhone {
/**
* 手机品牌名称
*/
private String name;
/**
* 展示手机信息
*
* @return
*/
public void showInfo() {
System.out.println("Hi,you has ordered a/an " getName());
}
}
//代理商,淘宝或是天猫等
public class SimpleIPhoneAgent {
/**
* 订购一个IPHONE
* @return
*/
public AbstractPhone orderPhone(){
return SimplePhoneFactory.produce(PhoneType.PHONE_TYPE_IPHONE);
}
}
//静态工厂
public class SimplePhoneFactory {
/**
* 生产
*
* @return
*/
public static AbstractPhone produce(PhoneType type) {
AbstractPhone phone = null;
switch (type) {
case PHONE_TYPE_XIAOMI:
phone = new Xiaomi();
break;
case PHONE_TYPE_IPHONE:
phone = new IPhone();
break;
default:
break;
}
return phone;
}
}
//iPhone
public class IPhone extends AbstractPhone {
public IPhone() {
setName(PhoneType.PHONE_TYPE_IPHONE.getType());
}
}
//枚举类
@AllArgsConstructor
@Getter
public enum PhoneType {
/**
* 手机品牌类型
*/
PHONE_TYPE_IPHONE("iPhone"),
PHONE_TYPE_XIAOMI("Xiaomi");
private String type;
}
测试
/**
* 简单工厂
*/
@Test
public void testX1(){
AbstractPhone iphone = new SimpleIPhoneAgent().orderPhone();
iphone.showInfo();
//Hi,you has ordered a/an iPhone
}
简单工厂(静态工厂),虽然不是真正的设计模式,但不失为一个简单的方法,可以将客户程序从具体类解耦。
工厂方法模式
定义了一个创建对象的接口,但是由子类决定要实例化的类是哪一个。工厂方法模式是让类把实例化推迟到子类。
在之前的场景下,我们有很多的代理商都从同一个工厂订购手机。但是如果生产手机的工厂由于设备老旧或是资金不足无法生产新型手机怎么办?以生产新的iphone7和小米6手机为背景,我们来看下工厂方式模式怎么实现的?
代码语言:javascript复制//抽象的手机代理商类中声明了一个工厂方法,供子类实现具体的生产方式。
abstract class AbstractAgent {
/**
* 订购
* @return
*/
public AbstractPhone orderPhone(PhoneType type){
//调用子类实现的工厂方法
return producePhone(type);
}
/**
* 声明工厂方法
* @param type
* @return
*/
abstract AbstractPhone producePhone(PhoneType type);
}
//新的工厂
public class NewPhoneFactory {
/**
* 生产
*
* @return
*/
public static AbstractPhone produce(PhoneType type) {
AbstractPhone phone = null;
switch (type) {
case PHONE_TYPE_XIAOMI_6:
phone = new Xiaomi6();
break;
case PHONE_TYPE_IPHONE_7:
phone = new IPhone7();
break;
default:
break;
}
return phone;
}
}
//京东代理商选择新的工厂来生产手机
public class JDAgent extends AbstractAgent {
/**
* 声明工厂方法
*
* @param type
* @return
*/
@Override
AbstractPhone producePhone(PhoneType type) {
return NewPhoneFactory.produce(type);
}
}
测试
/**
* 工厂方法模式
*/
@Test
public void testX2(){
AbstractPhone iphone7 = new JDAgent().orderPhone(PhoneType.PHONE_TYPE_IPHONE_7);
iphone7.showInfo();
//Hi,you has ordered a/an iPhone 7
}
抽象工厂模式
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
顾名思义,抽象工厂就是把工厂通过接口或是超类抽象定义出来,以满足不同手机的生产可以依赖不同工厂,但是对于调用方无感,只需要传入对应的工厂实例即可。
之前提供一系列的静态工厂实例类来直接实现代理商所需手机的生产工作(如前文的SimplePhoneFactory、NewPhoneFactory)。考虑到一旦工厂不再生产对应的手机了,我们就必须得修改多处代码,一个代理商的在生产前的工厂调用,一是工厂实例内的switch选择。
我们改造下上面的工厂方法模式,将工厂对象抽象出来,
代码语言:javascript复制//工厂的抽象接口定义
public interface PhoneFactory {
/**
* 生产
* @return
*/
AbstractPhone produce(PhoneType type);
}
//富士康工厂定义
public class FushikangFactory implements PhoneFactory {
/**
* 生产
*
* @return
*/
@Override
public AbstractPhone produce(PhoneType type) {
AbstractPhone phone = null;
switch (type) {
case PHONE_TYPE_IPHONE_8:
phone = new IPhone8();
break;
case PHONE_TYPE_IPHONE_X:
throw new IllegalArgumentException("NOT SUPPOT IPHONE X");
default:
break;
}
return phone;
}
}
//代理商基类补充工厂属性定义(Lombok自动生成了全参构造器和GetterSetter方法)
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class AbstractAgent {
/**
* 服务于抽象工厂模式
*/
private PhoneFactory factory;
/**
* 订购
* @return
*/
public AbstractPhone orderPhone(PhoneType type){
//调用子类实现的工厂方法
return producePhone(type);
}
/**
* 声明工厂方法
* @param type
* @return
*/
protected abstract AbstractPhone producePhone(PhoneType type);
}
//新的代理商苏宁易购
public class SuningAgent extends AbstractAgent {
//提供入口传入使用的工厂实例
public SuningAgent(PhoneFactory factory) {
super(factory);
}
/**
* 声明工厂方法
*
* @param type
* @return
*/
@Override
protected AbstractPhone producePhone(PhoneType type) {
return getFactory().produce(type);
}
}
测试
/**
* 抽象工厂模式
*/
@Test
public void testX3(){
SuningAgent suningAgent = new SuningAgent(new FushikangFactory());
AbstractPhone phone = suningAgent.orderPhone(PhoneType.PHONE_TYPE_IPHONE_8);
phone.showInfo();
phone = suningAgent.orderPhone(PhoneType.PHONE_TYPE_IPHONE_X);
phone.showInfo();
//Hi,you has ordered a/an iPhone 8
//java.lang.IllegalArgumentException: NOT SUPPOT IPHONE X
}
抽象工厂的接口定义中,还可以定义不同的产品系列,这是它相对于工厂方法模式,更容易扩展的地方。
总结
工厂方法模式特点
- 工厂方法模式创建对象,需要扩展一个类,并覆盖它的工厂方法。比如我们的
TaobaoAgent
,重写了AbsractAgent
中的produce
方法。 - 工厂方法模式通过继承,将对象的创建委托给子类,子类实现工厂方法来创建对象
- 工厂方法模式的作用:创建对象,而且是通过子类来创建对象,用这种方式,客户无需关注子类的具体实现,只需要关注他们的超类,抽象类型定义即可。
抽象工厂模式特点
- 抽象工厂模式的抽象级别更高,因此使用的场景也更宽广,可以用来创建一个产品家族的抽象类型,这个类型的子类定义产品被产生的方法。实例化工厂后,传入一些针对针对抽象类型定义的子类中,将一群相关的产品集合起来。和工厂方法模式一样,是用来解耦客户和所使用的多种具体产品的。
- 抽象工厂接口定义的生产方法,每个方法对应一个产品组。是工厂方法模式在产品类型纬度上的扩展。
- 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露的方法中
- 抽象工厂创建相关对象家族,而不需要依赖他们的具体类
相同点
- 实现客户和产品的解耦
- 允许类将实例化延迟到子类
- 将对象的创建封装起来,以便得到更松耦合、更有弹性的设计
设计基础
- 抽象
- 封装
- 多态
- 继承
OO原则
- 多用组合,少用继承
- 针对接口编程,而不针对实现编程
- 为交互对象之间松耦合设计而努力
- 类应该对扩展开发,对修改关闭
- 依赖抽象,不要依赖具体实现类
REFERENCES
《Head First》读书笔记