工厂模式提升之路--"无痴迷,不成功"
上一篇中聊了工厂模式的进化过程,了解了从简单工厂到工厂方法再到抽象工厂的全过程以及进化原因。进化到抽象工厂确实解决耦合等问题,但是会导致我们扩展时需要修改多个工厂相关类。很不优雅,不符合我们懒人的气质。
没了解过工厂模式进化之路的客官请先移步: 工厂模式进化之路
接下来我们聊聊怎么样才能既保留抽象工厂的优点,又解决它的缺点。提前声明一下:工厂模式已经讲完了,下面的不是某一种工厂了,属于工厂的运用优化。如果进化之路讲的是某个技能的介绍,那这篇文章讲的就是连招的使用!
首先我们在多个方面总结对比一下三个工厂模式:
- 在实现方式上:简单工厂抽象了产品类,工厂方法抽象了工厂类,抽象工厂是让抽象的工厂中有了多个分类创建方法。
- 在功能上:简单工厂实现了对象创建与松耦合,工厂方法使工厂更加有条理并且符合了“开闭原则”,抽象工厂对工厂进行了分类,合并和整理。
注:本文代码中类的关系继续沿用上一篇文章
工厂模式进化之路
抽象工厂与简单工厂相结合(1、3连招)
使用简单工厂解决抽象工厂的问题:”增加时需要改的地方多的问题“
抽象工厂与简单工厂结合的代码:
public class KeyboardFactory{
private static final String name = "XM"
//private static final String name = "HW"
...其他品牌的名字。
public Keyboard createKeyboard(){
Keyboard = null;
switch(name){
case "XM":
Keyboard = new XMkeyboard();
break;
case "HW":
Keyboard = new HWkeyboard();
break;
...
}
}
//MechanicalKeyboard是继承自Keyboard的。
public MechanicalKeyboard createMechanicalKeyboard(){
MechanicalKeyboard = null;
switch(name){
case "XM":
MechanicalKeyboard = new XMMechanicalKeyboard();
break;
case "HW":
MechanicalKeyboard = new HWMechanicalKeyboard();
break;
...
}
}
}
客户端使用:
class Xiaomi{
...
private void useKeyboard(){
...
KeyboardFactory factory = new KeyboardFactory();
Keyboard 键盘实例 = factory.createKeyboard();
Keyboard 机械键盘实例 = factory.createMechanicalKeyboard();
...
}
...
}
通过上面的连招,没有了简单工厂管理无序的问题,也解决了抽象工厂扩展时需要增加很多类的问题。而且也不需要像简单工厂一样需要传递参数了,因为参数在类中写死了。
但是我们发现,在这种结合方式中,如果进行扩展会和简单工厂一样违反“开闭原则”。所以我们想一下如何才能去除switch case语句让他可以自己去找需要实例化的类。
反射+抽象工厂(身法+大招)
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为反射机制。
简单一点来说:我可以用一个字符串通过反射获取任意一个类。
改造代码:
public class KeyboardFactory{
private static final String route = "某一个种类的实体类所在的包路径";
private static final String name = "XM";
//private static final String name = "HW"
...其他品牌的名字。
public Keyboard createKeyboard(){
// 通过反射机制,获取Class,通过Class来实例化对象
Class c = Class.forName(route+"."+name+"keyboard");
// 重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
return c.newInstance();
}
//MechanicalKeyboard是继承自Keyboard的。
public MechanicalKeyboard createMechanicalKeyboard(){
// 通过反射机制,获取Class,通过Class来实例化对象
Class c = Class.forName(route+"."+name+"MechanicalKeyboard");
// 重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
return c.newInstance();
}
}
通过上面的结合方式我们就可以完全去除switch case 语句了,还使代码更加简洁了。而且,name是可以作为参数进行传递的,这样我们之后要进行任何扩展都只改name的值就可以了。
此时还有一个问题没有解决:name的值哪里来?难道只能写死吗?写死也太不灵活了对吧
配置文件、数据库(辅助加成)
为了灵活 方式有多种:
- 配置文件
- 数据库
等任何存储性组件,个人更倾向于数据库。
我们只要让程序可以在某个地方读取到name的值就可以。
至此工厂模式的讨论结束。工厂模式实现了比较完善的功能闭环。基本的问题都已经解决,期待大家可以在未来通过工厂模式使自己的代码更上一层楼。希望我的一点总结可以帮助到大家。
期待大家点赞加关注