背景
我认为无论学习什么模式,都应该首先知道它的由来(也就是为了解决什么样的问题)。这能为我们在学习它的时候提供一个方向,这很重要!
情景:富士康生产苹果手机,规格有13min、13、13pro、13proMax(它们大部分功能是一样的,但是配置和外形上有区别)。在不考虑任何模式的情况下,客户端1伪代码应该是这样的:
if(13min)
new 13min()
if(13)
new 13()
if(13pro)
new 13pro()
工厂设计模式:在某些情况下,对象的创建很复杂,可能需要某种程度的抽象,以便客户端代码不了解这些复杂性和内部实现细节。 在某些情况下,对象的创建在代码的各个部分中是分散的和重复的。
从这句话可以得出,如果实例化过于复杂(不是简单的,new 13());或者有多个调用者,多个new 语句,分散各处。那么我们就应该统一一个地方去创建对象,提供接口给调用者。
还有一个问题就是如果需要增加一个手机规格,那么所有调用者处都可能需要增加代码。
另一个问题是,客户端应用程序必须知道多少类型的具体类可以预先使用。
ps:这句话我就不是很理解,不管怎么说调用者都应该知道提供者能够提供什么类型啊。就好像给了我一个工具,还能不告诉我它干什么的?也许它指的是别的什么意思?
普通工厂
场景:富士康生产苹果手机,规格有13min、13、13pro(13proMax太贵了不考虑,哈哈!)。
不同规格苹果手机的共同接口:
public interface Iphone {
String getName();
void callNumber();
void sendMessage(String message);
void playgames(int gameName);
}
具体规格的手机实现类
iphone13:
public class Iphone13 implements Iphone{
@Override
public String getName() {
return "iphone13";
}
@Override
public void callNumber() {
//todo 打电话
}
@Override
public void sendMessage(String message) {
//todo 发短信
}
@Override
public void playgames(int gameName) {
//todo 玩游戏
}
}
iphone13min:
public class Iphone13min implements Iphone{
@Override
public String getName() {
return "iphone13min";
}
@Override
public void callNumber() {
//todo 打电话
}
@Override
public void sendMessage(String message) {
//todo 发短信
}
@Override
public void playgames(int gameName) {
//todo 玩游戏
}
}
iphone13pro(略)
定义工厂去生产他们
public class SimpleFactory {
public static Iphone createIphone(String phoneName) {
Iphone iphone = null;
if ("Iphone13min".equals(phoneName)) {
//todo 有自己的制造工艺,可能不是这么简单
iphone = new Iphone13min();
}
if ("Iphone13".equals(phoneName)) {
//todo 有自己的制造工艺,可能不是这么简单
iphone = new Iphone13();
}
if ("Iphone13pro".equals(phoneName)) {
//todo 有自己的制造工艺,可能不是这么简单
iphone = new Iphone13pro();
}
//如果要加一个规格,在这里添加
return iphone;
}
}
测试
/**
* 描述:测试简单静态工厂
* <p>作者: aliyu
* <p>创建时间: 2021-10-15 6:01 下午
*/
public class TestSimpleFactory {
public static void main(String[] args) {
Iphone iphone13 = SimpleFactory.createIphone("Iphone13");
System.out.println("生产的是 " + iphone13.getName());
Iphone iphone13min = SimpleFactory.createIphone("Iphone13min");
System.out.println("生产的是 " + iphone13min.getName());
}
}
ps:这样的代码,对象的生产交由工厂完成,没有重复的new 语句。同时也满足,如果新增手机规格,调用方代码不需要更改。
结果:
工厂方法
什么叫“工厂方法”?多了“方法”两个字意味着什么?
简单工厂是:新建一个工厂类,由统一的工厂类生产不同规格的手机。但是如果有新的规格产生,我们就必须修改这个工厂类:
public class SimpleFactory {
public static Iphone createIphone(String phoneName) {
Iphone iphone = null;
if ("Iphone13min".equals(phoneName)) {
//todo 有自己的制造工艺,可能不是这么简单
iphone = new Iphone13min();
}
if ("Iphone13pro".equals(phoneName)) {
//todo 有自己的制造工艺,可能不是这么简单
iphone = new Iphone13pro();
}
//如果要加一个规格,在这里添加
if ("Iphone13proMax".equals(phoneName)) {
//todo 有自己的制造工艺,可能不是这么简单
iphone = new Iphone13proMax();
}
}
}
不同规格的手机在同一个工厂里面生产,如果新增一个规格,可能会影响其它规格的手机(代码中体现在,都在同一个类的方法中)。那么有没有办法解决这个问题呢?答案是有的,干脆不同的规格有不同的创建工厂,再抽出一个工厂接口类给调用者。
抽出的工厂接口:
/**
* 描述:工厂方法接口
* <p>作者: aliyu
* <p>创建时间: 2021-10-19 2:18 下午
*/
public interface MethodFactory {
Iphone createIphone();
}
不同规格的工厂类实现:
/**
* 描述:专门生产13的工厂
* <p>作者: aliyu
* <p>创建时间: 2021-10-19 2:20 下午
*/
public class Iphone13Factory implements MethodFactory{
@Override
public Iphone createIphone() {
//todo 实例化实际可能不是这么简单
return new Iphone13();
}
}
/**
* 描述:专门生产13min的工厂
* <p>作者: aliyu
* <p>创建时间: 2021-10-19 2:20 下午
*/
public class Iphone13minFactory implements MethodFactory{
@Override
public Iphone createIphone() {
//todo 实例化实际可能不是这么简单
return new Iphone13min();
}
}
PS:工厂模式中核心工厂接口类不负责具体实例的创建,而是将具体的创建,交由对应规格的工厂类(实现核心工厂接口)去完成。
调用者调用:
public class TestMethodFactory {
public static void main(String[] args) {
//选择实际工厂
MethodFactory iphone13Factory = new Iphone13Factory();
//交由实际工厂实例化
Iphone iphone13 = iphone13Factory.createIphone();
//调用实例方法
iphone13.getName();
MethodFactory iphone13minFactory = new Iphone13minFactory();
Iphone iphone13min = iphone13minFactory.createIphone();
iphone13min.getName();
}
}
其它
抽象的思想是为了抽出公共的东西,避免代码的重复。但实际上如果抽象层次过多,程序会变得异常复杂。
本来是想写抽象工厂模式的,但是发现很多文章,认为的是,我上面写的“工厂方法”就是抽象方法。大概是认为把工厂抽象了就是抽象工厂?闹不明白,再说了。
参考和注脚
工厂设计模式解决什么问题_使用工厂模式解决设计问题
JAVA设计模式之工厂模式—Factory Pattern
很多文章都提到了“客户端”这个概念,却没有哪里有过解释,也许是我孤陋寡闻了。以我多次遇见的理解,可以解释为实际“调用者”。熟悉分布式的同学,肯定了解服务提供者和消费者的概念。更细致地分,可以是方法提供者,方法调用者。所以我就用调用者来描述了。 ↩︎