1、概念
代码编写出来是为了给别人(Client)调用的:
- 调用者(client)跟代码编写者(provider),可能是同一个人,也可能是不同的人
- 提供给调用者的代码,有可能是源码可见的,也可能是源码不可见、不可修改的(比如 jar 包)。
所以,为了简化代码的协作使用及管理维护,必须想尽办法简化代码逻辑,实现必要的【分离】。下面我分别介绍几种工厂模式。
2、最原始的工厂模式
我们使用手机来作为测试,首先上代码图:
public class Iphone {
public void call () {}
}
public class Huawei {
public void call () {}
}
public class Oppo{}
public class XiaoMi {}
public class Vivo {}
最原始的方式:如果我们要创建多个不同品牌的手机, 那么就要 new 多个不同品牌的手机类,如果这样的代码提供给客户端调用,必须要将所有类的名称以及对应的方法暴露给客户端,这样的方式非常原始,也很简单,但是代码的逻辑不清晰,暴露的内容过多。为此,我们有一个解决方案:抽象逻辑(抽取其中的一些逻辑),提供一个 接口 这样 可以减少方法调用的复杂度,也便于抽象跟代码管理。
2.1、有了接口之后
代码图:
public interface Phone {
void play();
}
所有手机类都实现 Phone 接口,然后将暴露给客户端调用的逻辑都封装在 play 方法里,那么客户端(调用者)需要知道的 API 就减少到了两种:Phone 的接口信息;Phone 的接口有哪些实现类。
但是这种工厂模式的缺点也很大:
- 客户端(调用者),必须知道手机类的具体名称,如果有 1000 个手机需要调用,则我们需要记住 1000 个手机的名称 ,这对于调用者来说不可能的。
- 客户端的调用,跟提供的代码是耦合的,例如有一天手机新增了一个功能,然后我们修改了接口的方法,同时成百上千个手机类也实现了这个接口,则全部手机类都是需要修改的,其 可扩展性 就减少了,维护的成本也随之增加了。
3、简单工厂
通过原始工厂模式的演变,为了更好的符合实际环境。
原理:通过在接口与实现类中间添加一层 ,来解决需要记住名称过多和耦合问题。我们只要记住,代码耦合的话,可以通过两者之间添加一层来解决。
代码图:
public class PhoneFactory {
public Phone createPhone(String tag) {
if ("iphone".equals(tag)) {
return new Iphone();
} else if ("vivo".equals(tag)) {
return new Vivo();
} else if ("huawei".equals(tag)) {
return new Huawei();
}
}
}
我们在原始工厂模式的基础上,再创建一个 PhoneFactory 类,里面只定义一个专门生产手机的方法,通过客户端调用者传进来的 手机品牌 然后生产对应的手机。
然后客户端调用者就可以直接调用相应的手机品牌的方法了:
PhoneFactory pf = new PhoneFactory();
pf.createPhone("iphone").play();
pf.createPhone("vivo").play();
pf.createPhone("huawei").play();
简单工厂模式,本身已经为解耦合做出了很好的方案。但是事情都是有两面性的,所以它也有缺点:
- PhoneFactory 类代码跟 Phone 代码 有耦合
- 每次要 添加/删除/修改 某一个 Phone,都需要修改 PhoneFactory 这个类
为此,简单工厂继续演变,就有工厂方法模式。
4、工厂方法模式
在简单工厂的基础上,首先为 Phone 专门建立一个生产手机的 工厂 (父工厂)。
public interface PhoneFactory {
Phone createPhone();
}
如果我们需要增加一款产品,比如是 iPhone ,那么,只需要建立一个只生产 iPhone 的(子)工厂就可以了;如果再增加一款产品,比如 HuaWei,那么只需要增加另外一条只生产 HuaWei 手机的(子)工厂就可以了 。
public class HuaweiFactory implements PhoneFactory {
public phone createPhone() {
return new Huawei();
}
}
客户端调用者,只需其中一个手机品牌工厂实现 Phone 工厂(接口)就可以了,就是一个手机品牌对应一个手机工厂,从而达到了解耦合。
PhoneFactory huaWeiPf = new HuaweiFactory();
huaWeiPf .createPhone().play();
PhoneFactory iPhonePf = new IphoneFactory();
iPhonePf .createPhone().play();
工厂方法模式的缺点:
- 代码容易膨胀,例如:我们每增加一个手机产品,相应的也要增加一个子工厂。
- 不容易反应产品与产品之间的关系。
事情是两面性的,耦合解决了也会产生另外一方面的问题,所以就要根据实际情况选择相应的对策来。就工厂方法模式来说,是目前最标准的一种工厂模式,也是应用广泛的一种模式了。