1.简单工厂
郭大神和盘哥在说好想买ipad,用代码实现一下
不用模式的设计
public interface Product { String getDesc(); } public class IpadProduct implements Product { @Override public String getDesc() { return "Ipad2"; } } public class Client { public static void main(String[] args) { Product product = new IpadProduct(); System.out.println("郭大神说好想买" + product.getDesc() + ",但没钱!!!"); product = new IpadProduct(); System.out.println("盘哥说我好想买" + product.getDesc() + ",但没钱!!!"); } }
有何问题?
如果想买的iphone5时怎么办?
得找到所有new IpadProduct改成new IphoneProduct
而且改的是客户端的代码
应用模式的设计
public class IphoneProduct implements Product { @Override public String getDesc() { return "Iphone5"; } } public class SimpleFactory { public static Product getProdcut() { return new IpadProduct(); } } public class Client { public static void main(String[] args) { Product product = SimpleFactory.getProduct(); System.out.println("郭大神说好想买" + product.getDesc() + ",但没钱!!!"); product = SimpleFactory.getProduct(); System.out.println("盘哥说我好想买" + product.getDesc() + ",但没钱!!!"); } }
无模式遇到的问题如何解决?
只要在SimpleFactory.getProdcut()换成IphoneProdcut即可
优点
创建对象收拢,扩展性加强了。
扩展时不需要修改客户端代码。
对象创建与使用解耦
缺点
多出一个工厂类
2.工厂方法
应用工厂方法模式的设计
abstract public class Person { private String name; Person(String name){ this.name = name; } abstract Product getProduct(); public void say() { Product product = getProduct(); System.out.println(name + "说我好想买" + product.getDesc() + ",但没钱!!!"); } } public class GuoPerson extends Person { public GuoPerson(){ super("郭大神?); } @Override Product getProduct() { return new IpadProduct(); } } public class ShenPerson extends Person { public ShenPerson(){ super("盘哥"); } @Override Product getProduct() { return new IpadProduct(); } } public class ModeClient { public static void main(String[] args) { Person person = new GuoPerson(); person.say(); person = new ShenPerson(); person.say(); } }
优点
可以在不知道具体产品下实现编程
可以在不改变业务代码上很容易扩展出一个新的产品
缺点
多出几个具体工厂类
3.抽象工厂
考虑一下生产ipad和iphone的一个程序
工厂模式的设计有何问题?
public interface Ipad extends Product { } public class HongKongIpad implements Ipad { @Override public String getDesc() { return "香港版的Ipad"; } } public class USAIpad implements Ipad { @Override public String getDesc() { return "美国版的Iphone"; } } public interface Iphone extends Product { } public class HongKongIphone implements Iphone { @Override public String getDesc() { return "香港版的Iphone"; } } public class USAIphone implements Iphone { @Override public String getDesc() { return "美国版的Iphone"; } } public class SimpleFactory { public static Ipad createIpad(int type) { if (type == 1) { return new HongKongIpad(); } return new USAIpad(); } public static Iphone createIphone(int type) { if (type == 1) { return new HongKongIphone(); } return new USAIphone(); } } public class Client { public static void main(String[] args) { Ipad ipad = SimpleFactory.createIpad(1); Iphone iphone = SimpleFactory.createIphone(1); System.out.println("我采购的是:" + ipad.getDesc() + "和" + iphone.getDesc()); // ipad = SimpleFactory.createIpad(2); // iphone = SimpleFactory.createIphone(2); // System.out.println("我采购的是:" + ipad.getDesc() + "和" + iphone.getDesc()); } }
工厂模式的设计有何问题?
客户端想只买一家公司的产品,客户端需要了解每种类型是出自哪一家公司?
港版ipad与iphone是出自同一家公司,美版ipad与iphone也是出自同一样公司,对于采购时
它只要报一下要什么哪个品牌的就可以买到同一个品牌的ipad和iphone
应用抽象工厂模式的设计
public interface AppleFactory { Ipad createIpad(); Iphone createIphone(); } public class HongKongCompanyAppleFactory implements AppleFactory { @Override public Ipad createIpad() { return new HongKongIpad(); } @Override public Iphone createIphone() { return new HongKongIphone(); } } public class USACompanyAppleFactory implements AppleFactory { @Override public Ipad createIpad() { return new USAIpad(); } @Override public Iphone createIphone() { return new USAIphone(); } } public class ModeClient { public static void main(String[] args) { AppleFactory appleFactory = new HongKongCompanyAppleFactory(); Ipad ipad = appleFactory.createIpad(); Iphone iphone = appleFactory.createIphone(); System.out.println("我采购的是:" + ipad.getDesc() + "和" + iphone.getDesc()); // appleFactory = new USACompanyAppleFactory(); // ipad = appleFactory.createIpad(); // iphone = appleFactory.createIphone(); // System.out.println("我采购的是:" + ipad.getDesc() + "和" + iphone.getDesc()); } }
优点
缺点
深度思考
spring容器是最大的工厂
BeanFactory.getBean("xxx")
FileSystemXmlApplicationContext/ClassPathXmlApplicationContext
下面吐槽一下郭大神
考虑一下郭大神的家谱
...
郭大神的祖父
|
郭大神的父亲
|
郭大神
|
郭大神的儿子
...
郭大神的祖父创造了郭大神的父亲,郭大神的父新创造了郭大神,郭大神创造了郭大神的儿子
盘哥这边呢
...
盘哥的祖父
|
盘哥的父亲
|
盘哥
|
盘哥的儿子
...
用类来表示
interface Grandfather{ Father createFather(); } interface Father{ Self createSelf(); } interface Self { Son createSon(); }
如果我把郭明先生的祖父换成了姓郑(盘哥姓郑),则
当我换掉Grandfather时,奇迹出现了,整个家族的姓都变了。
从上面来看其实是工厂模式与工厂模式组合使用,经过组合后我们能写出具备极其扩展性的代码。
上面的原理是把工厂抽象成是一个产品(就是把工厂也当成是产品,那么生产这个产品的就是工厂的工厂。),于是就出现了"工厂的工厂的工厂"生产出"工厂的工厂","工厂的工厂"生产"工厂",最后工厂生成产品。这样就是级联使用工厂。
我们可以在任意一个环节替换实现,于是可以产生一个从大到小的扩展点。
考虑一下我们的建站平台框架的设计
站点有站点的接口, 同时还有站点资源接口(静态资源:js,css), 站点渲染接口, 站点数据接口。站点数据又可以分为站点数据读取接口, 和站点数据变更接口。
如果我设计下面这样的一个接口
interface SiteAPIFactory { getSiteAPI(); } interface SiteAPI{ getSiteResourceAPI(); getSiteDataAPI(); getRenderAPI(); } interface SiteDataAPI{ getSiteDataReadAPI(); getSiteDataDesignAPI(); } interface SiteDataReadAPI{ getSiteData(); } interface SiteDataDesignAPI{ updateSiteData(); deleteSiteData(); }
经过上面这样设计之后, 如果我替换了SiteAPIFactory的实现, 我就可以操控整个站点的所有接口实现, 当我只替换SiteDataAPI时我能控制站点读取与写入数据的实现。也就是说从大到小的粒点扩展点我都支持了。做为平台的设计, 应用就可以根据需要自行替换实现。从而达到各种粒度的扩展。
很多模式都能组合起来使用,同时许多模式能与自身组合使用,像工厂,桥接等等
同时如果我写个SiteAPIUtil的获取SiteAPIFacotry,于是我就能提供所有接口了
SiteAPIUtil{ SiteAPIFactory getSiteAPIFactory(){ DefaultSiteAPIFactory.instance }; SiteAPI getSiteAPI(){ getSiteAPIFactory().getSiteAPI(); }; SiteDataAPI getSiteDataAPI(){ getSiteAPI().getSiteDataAPI(); }; SiteDataDesignAPI getSiteDataDesignAPI(){ getSiteDataAPI().getSiteDesignAPI(); } ... }
再认真思考一下,你会发现SiteAPIUtil其实也是一个简单工厂,可以命名为SiteAPISimpleFactory
4.模板方法
考察一下郭大神与盘哥是如何上班的
不用模式的设计
public interface Person { String getName(); void summary(); } public class GuoPerson implements Person { @Override public void summary() { System.out.println(getName() + "起床"); System.out.println(getName() + "坐公交去上班"); System.out.println(getName() + "上班到9点时上了一次厕所"); System.out.println(getName() + "上班到10点时上了一次厕所"); System.out.println(getName() + "上班到11点时上了一次厕所"); System.out.println(getName() + "坐公交回到家"); System.out.println(getName() + "洗个澡睡觉了"); } @Override public String getName() { return "郭大神"; } } public class ShenPerson implements Person { @Override public void summary() { System.out.println(getName() + "起床"); System.out.println(getName() + "骑自行车去上班"); System.out.println(getName() + "上班到10点时上了一次厕所"); System.out.println(getName() + "骑自行车回到家"); System.out.println(getName() + "洗个澡睡觉了"); } @Override public String getName() { return "盘哥"; } } public class Client { public static void main(String[] args) { Person person = new GuoPerson(); person.summary(); person = new ShenPerson(); person.summary(); } }
不用模式有何问题?
他们两个上班过程基本上是一样的, 出现了重复的代码。
再考察一个人得同样要写很多重复的代码。
应用模式的设计
abstract public class AbstractPerson implements Person { public void summary() { System.out.println(getName() + "起床"); goToCompany(); work(); backToHome(); System.out.println(getName() + "洗个澡睡觉了"); } abstract public void goToCompany(); abstract public void work(); abstract public void backToHome(); } public class GuoPerson extends AbstractPerson { @Override public String getName() { return "郭大神"; } @Override public void goToCompany() { System.out.println(getName() + "坐公交去上班"); } @Override public void work() { System.out.println(getName() + "上班到9点时上了一次厕所"); System.out.println(getName() + "上班到10点时上了一次厕所"); System.out.println(getName() + "上班到11点时上了一次厕所"); } @Override public void backToHome() { System.out.println(getName() + "坐公交回到家"); } } public class ShenPerson extends AbstractPerson { @Override public String getName() { return "盘哥"; } @Override public void goToCompany() { System.out.println(getName() + "骑自行车去上班"); } @Override public void work() { System.out.println(getName() + "上班到10点时上了一次厕所"); } @Override public void backToHome() { System.out.println(getName() + "骑自行车回到家"); } } public class ModelClient { public static void main(String[] args) { Person person = new GuoPerson(); person.summary(); person = new ShenPerson(); person.summary(); } }
优点
没有重复的代码了,新增一个人时也同样减少一直重复的代码
如果在上班完加一个先吃个饭再回家,则只需要在抽象类里加上这行代码,无需改动所有Person
缺点
多出一个类,有时多出好几个方法
深度思考
模式方法模式其实就是一个抽象的过程,是最小抽象,位于抽象的最低层
与工厂方法的区别
总结:
简单工厂能把具体实现包装起来,让客户端真正达到面向接口编程
工厂方法可以在高层进行编码,让服务端的产品线真正达到面向接口编程
抽象工厂能聚合整个产品簇,让整个服务端的多个产品线真正达到面向接口编程
模板方法同样是在高层进行编码,也同样是面向接口编程。
但工厂方法及抽象工厂方法着重抽象的是产品,而模板方法着重抽象的是步骤。
而我们通常会两者一起结合起来使用。
思考上面那个模板模式,你会发现去上班和回到家代码很相似,
于是我们组合使用工厂模式又能去除重复代码。
在抽象类返回一个交通工具,上下班和回到家就可以在基类编程了。
代码如下:
public interface Vehicle { String by(); } public class Bus implements Vehicle { @Override public String by() { return "坐公交车"; } } public class Bike implements Vehicle { @Override public String by() { return "骑自行车"; } } abstract public class AbstractPerson implements Person { public void summary() { Vehicle vehicle = getVehicle(); System.out.println(getName() + "起床"); System.out.println(getName() + vehicle.by() + "去上班"); work(); System.out.println(getName() + vehicle.by() + "回到家"); System.out.println(getName() + "洗个澡睡觉了"); } abstract public Vehicle getVehicle(); abstract public void work(); } public class GuoPerson extends AbstractPerson { @Override public String getName() { return "郭大神"; } @Override public void work() { System.out.println(getName() + "上班到9点时上了一次厕所"); System.out.println(getName() + "上班到10点时上了一次厕所"); System.out.println(getName() + "上班到11点时上了一次厕所"); } @Override public Vehicle getVehicle() { return new Bus(); } } public class ShenPerson extends AbstractPerson { @Override public String getName() { return "盘哥"; } @Override public void work() { System.out.println(getName() + "上班到10点时上了一次厕所"); } @Override public Vehicle getVehicle() { return new Bike(); } }
这样,在AbstractPerson的子类只需要返回交通工具, 就可以少掉两个方法