概述
由于java具有封装、继承、多态的特性,我们都会对我们定义的对象进行封装,在使用这些对象之前,我们都会创建对象,也就是我们常用的new XXObject()
方式。
当我们是自己封装的对象,我们自己使用的时候是知道怎么创建这个对象的,因为这个对象实现者是我们自己,如下图所示,张三使用自己定义的对象,可以随意创建对象。
当我们是多人合作的,张三还可能使用李四定义的对象,或者需要引入第三方Jar包中的对象,则变成下面这种方式。
思考,张三是使用其他人定义的对象或者其他jar包对象,如何创建这些对象呢?这些对象实现细节对张三来说,是一个黑盒,那么我们简单抽象简化一下这个问题,分为接口提供者(创建对象者理解为提供一个接口),使用接口者(使用对象者理解为使用一个接口)
想获取以上对象,我们是否可以使用某种方式统一起来,或者尽可能让使用者使用起来方便容易一些,接下来我们将讨论一下这个问题。
案例拆分
第一个版本
假设A要提供一些手机对象,需要给B调用,如下图所示。手机对象是独立的对象,对于调用者B来说,需要知道每一个手机对象,才能调用使用
手机对象对应代码
public class HuaWei {
public String name() {
return "华为";
}
}
public class Oppo {
public String name() {
return "OPPO";
}
}
public class XiaoMi {
public String name() {
return "小米";
}
}
调用者B
public class B {
public static void main(String[] args) {
// B要先知道有HuaWei这个手机东西,B才能使用
HuaWei huaWei = new HuaWei();
huaWei.name();
// B要先知道有XiaoMi这个手机东西,B才能使用
XiaoMi xiaoMi = new XiaoMi();
xiaoMi.name();
}
}
这种实现方式,给调用者B体验十分不友好,等于B完完全全了解了一遍自己要使用的对象,而这些对象实现本该由A来完成。
抽象 + 简单工厂方法
A对手机对象进行抽象,提供一个抽象接口给B使用,避免B直接使用手机对象。如果只是进行抽象,能改善吗?如下图所示。
手机对象对应代码
public interface Phone {
String name();
}
public class HuaWei implements Phone {
public String name() {
return "华为";
}
}
public class Oppo implements Phone {
public String name() {
return "OPPO";
}
}
public class XiaoMi implements Phone {
public String name() {
return "小米";
}
}
调用者B
public class B {
public static void main(String[] args) {
// B要先知道有HuaWei这个手机东西,B才能使用
Phone huaWei = new HuaWei();
huaWei.name();
// B要先知道有XiaoMi这个手机东西,B才能使用
Phone xiaoMi = new XiaoMi();
xiaoMi.name();
}
}
我们发现这种实现方式,给调用者B来讲,情况和第一个版本没什么区别,尽管代码抽象为Phone huaWei = new HuaWei();
,对于B调用者来说,new HuaWei();
步骤也是要了解有HuaWei这个对象才能使用,实际上没有任何改善,还增加的代码的复杂度。
继续优化。A提供简单工厂方法实现
public class PhoneSimpleFcatory {
public static Phone getPhone(String type) {
if ("O".equals(type)) {
return new Oppo();
} else if ("H".equals(type)) {
return new HuaWei();
} else if ("X".equals(type)) {
return new XiaoMi();
}
return null;
}
}
A向B提供简单工厂方法使用API
手机类型参数入参 | 对应手机 |
---|---|
H | 华为 |
O | OPPO |
X | 小米 |
调用者B
public class B {
public static void main(String[] args) {
// B根据API获取HuaWei手机
Phone huaWei = PhoneSimpleFcatory.getPhone("H");
huaWei.name();
// B根据API获取XiaoMi手机
Phone xiaoMi = PhoneSimpleFcatory.getPhone("X");
xiaoMi.name();
}
}
由于A向B提供了一份API使用,B不用再关心是否有HuaWei、XiaoMi等手机对象,只需要传入参数就可以获取到自己想要的对象,对调用者使用友好。调用关系将变成下面这种调用关系。
大规模对象演进,工厂方法
随着手机对象不断增多,原来的方式API管理的对象越来越大,对于调用者来说,对于对象提供者A与对象使用者B成本都越来大,需要对设计进行二次抽象
引入工厂方法,引入分层思想,将结构分类,类似于工程模块分解。单从HuaWeiFactory
来说,这个小模块是抽象 + 简单工厂方法
,对于所有手机来说,这是二次抽象
HuaWeiFactory部分代码
public class HuaWei implements Phone {
public String name() {
return "华为";
}
}
public class HuaWei2 implements Phone {
public String name() {
return "华为2";
}
}
public class HuaWei3 implements Phone {
public String name() {
return "华为3";
}
}
public interface Factory {
Phone getPhone(String type);
}
public class HuaWeiFactory implements Factory{
@Override
public Phone getPhone(String type) {
if ("1".equals(type)) {
return new HuaWei();
} else if ("2".equals(type)) {
return new HuaWei2();
} else if ("3".equals(type)) {
return new HuaWei3();
}
return null;
}
}
调用者B
public class B {
public static void main(String[] args) {
Factory Factory = new HuaWeiFactory();
// B根据API获取HuaWei手机
Phone huaWei = Factory.getPhone("1");
huaWei.name();
// B根据API获取HuaWei2手机
Phone xiaoMi = Factory.getPhone("2");
xiaoMi.name();
}
}
通过模块分解,我们将大规模拆分为小规模,这也是我们做工程的思想
是否能不提供文档API
上述步骤中A向B提供简单工厂方法使用API
手机类型参数入参 | 对应手机 |
---|---|
H | 华为 |
O | OPPO |
X | 小米 |
需要A人工维护这份文档,那么我们能否不提供API,B也能知道,答案是肯定的,因为对于java中的接口来说,接口就是API,那么我们继续优化,继续抽象分层。
那我门的逻辑将变成以下关系,
具体代码表现,这里不再实现,给出B调用代码
public class B {
public static void main(String[] args) {
// 手机服务商
PhoneProvider provider = new PhoneProvider();
// 手机工厂
HuaWeiFactory huaWeiFactory = provider.getHuaWeiFactory();
// B获取HuaWei手机
Phone huaWei = huaWeiFactory.getHuaWei();
huaWei.name();
// B获取HuaWei2手机
Phone huaWei2 = huaWeiFactory.getHuaWei2();
huaWei2.name();
// 手机工厂
XiaoMiFactory xiaoMiFactory = provider.getXiaoMiFactory();
// B获取HuaWei手机
Phone xiaoMi = xiaoMiFactory.getXiaoMi();
xiaoMi.name();
}
}
最终形式,接口即API,A只向B提供PhoneProvider
,B根据接口方法自动选择自己想要的对象
Spring 注入思想
待续…