我们在程序中使用一个对象时,需要new一下,如果需要设置其他值就再初始化一下。比如我要使用一个按钮,手动new一个矩形按钮,然后初始化一些值,如显示文字,背景色等。
// 矩形按钮
IButton btn = new RecButton();
// 初始化其他值
btn.setText("提交");
btn.setBackgroundColor("#00aaff");
// 其他初始化省略
// 圆形按钮
IButton btn2 = new RoundButton();
btn.setText("关于");
btn.setBackgroundColor("#00aaff");
// 其他初始化省略 这样写有几个缺点: 1、写一次没有问题,如果需要100个,就要写100次,太麻烦了。 2、很多设置是重复的,比如例子中的背景色,一个系统可能风格统一,只有几种背景色,不需要每次都手动设置。 3、耦合性太强,客户端必须知道具体的按钮创建过程,必须会创建按钮才行,后续按钮的方法改变,客户端也可要跟着修改,如以后按钮必须设置大小了,所有客户端代码都要变动。 4、重复对象没有控制,比如btn2的关于,可能每个页面都有,但是每个页面的这个按钮都是一模一样的,没必要每次都创建一遍。 5、没有封装变化,假如写了100个new RoundButton,后续这个按钮发生改变了,我们要改100处代码。 等等,如果你再仔细想想,各种各样的情况下都有各种各样的缺点(当然这么写也有优点的,至少简单嘛,如何设计没有最好,只有合适的),那么我们有没有其他方式来规避这些问题呢?其实我们需要一个对象时,除了自己new之外,还有就是从其他地方获取,我们完全可以把这些按钮的创建过程放到一起,客户端使用的时候直接获取就行了。比如下面代码:
public class RoundButtonFactory implements Creater {
public static IButton createButton(String text) {
// 圆形按钮
IButton btn = new RoundButton();
btn.setText(text);
btn.setBackgroundColor("#00aaff");
return btn;
}
} 工厂模式定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。 客户端使用时,只需要调用createButton就行,屏蔽了底层的具体实现,后续实现类变化了,只需要修改这个方法就行了;也使客户端和具体的按钮实现类解耦开来,其实这就是最基本的工厂模式。 简单说就是将要使用的对象抽象出一个接口(产品),还有一个接口的创建工厂,每个具体实现类(产品的实现类)的创建由工厂的实现类创建。
2、结构与类图
工厂模式通用类图如下:
上面举的例子的类图如下:
工厂方法包含四个角色: 1、抽象产品(Product):负责定义产品公有属性; 2、具体产品(ConcreteProduct):具体的产品实现类; 3、抽象工厂(Creater):抽象的创建类,也就是抽象工厂; 4、具体工厂(ConcreteCreater):具体创建者,也就是具体工厂,负责具体产品实现类的创建。
3、适用场景及效果(优缺点)
没有工厂的时候,假如我们要做饭,需要用到火,创建火的同时发现需要用到木柴,还要创建一个锯来锯木柴......代码如下:
// 创建锯
Saw saw = new Saw ();
// 使用锯,锯木柴
FireWood fw = saw.cut();
// 使用木柴创建火
Fire fire = new WoodFire(fw); 可以看到,这样的话,做饭的逻辑就依赖了锯、木柴、火等东西,如果使用工厂呢
Fire fire = WoodFireFactroy.create();1、具有良好的封装性,逻辑代码清晰,不用new了,不用初始化了,只需要简单的get或者create就行了。 2、耦合性低,工厂模式是典型的解耦框架,屏蔽了具体产品类(都是用产品接口嘛),调用者无需关心底层如何实现,产品类变化时只要接口不变就不影响调用者,使客户端和具体产品解耦,更能屏蔽创建具体产品时需要的其他关联类(如做饭就不需要依赖锯了),符合迪米特法则,只和需要的类交流,也符合依赖导致原则,只依赖抽象,更符合里氏替换原则,使用产品子类代替父类,完全没问题。 3、扩展非常方便,新加一类产品时(如上面例子新增一种按钮),只需要新增一个工厂实现类即可。无需修改原有代码,达到了“拥抱变化”,符合开闭原则。 当产品创建简单,比较固定的时候,或者调用者个性化情况太多时,工厂就体现不出他的优势了,比如常用的List,我们就没必要弄个ListFactory.createArrayList(),徒增代码复杂性。还有种类比较固定,不会太多,没必要抽象出接口,那也不必非用工厂模式。最好在下面的情况下才考虑使用工厂模式: 1、调用者不需要直到具体的产品创建过程时; 2、调用者使用的对象存在变动的可能,甚至完全不知道使用哪个具体对象时。 3、需要做出灵活、可扩展的功能时,再考虑工厂模式,其实不一定所有功能都要做到可扩展,谨慎过度设计。 4、需要解耦时,减少调用者和具体实现类的依赖时。
4、示例和扩展
1、退化成简单工厂模式,当要创建的产品种类较少时,并且可以预见时,可以把工厂实现类合并到一起,对外提供一个静态工厂方法,比如上面的按钮例子中:
public static IButton crateButton(String type, String text) {
IButton btn = null;
if ("round".equals(type)) {
btn = new RoundButton(); // 圆形按钮
} else if ("rec".equals(type)) {
btn = new RecButton(); // 矩形按钮
} else {
return null ;
}
btn.setText(text);
btn.setBackgroundColor("#00aaff");
return btn;
} 这种简单工厂模式用起来非常简单,缺点是扩展困难,不符合开闭原则,要注意设计没有最好,只有适不适合,在可预见的变化下,简单工厂模式非常好用。 2、约束产品类实例数量,通常和其他模式组合能达到很多效果,比如使用工厂创建好对象后缓存起来,达到单例或多例的目的,比如下面单例工厂:
//单例工厂
public class SingletonFactory {
private static Map
, Object> objCache = new HashMap
, Object>(); public synchronized static Object getInstance(Class
clazz) throws Exception { Object singleton = objCache.get(clazz); if (singleton == null) { singleton = createInstance(clazz); objCache.put(clazz, singleton); } return singleton; } private static Object createInstance(Class
cl