秒懂设计模式——总述及工厂模式
【前言】每次一说道“设计模式”总会给人一种误区,认为这些东西,只有那些“技术大牛”才会玩的东西。但是给我的感觉却恰恰相反,它更应该是“非牛人”玩的东西。为什么这么说呢?打个比方吧,“设计模式”就好比武术中的“固定招式”,只有新手会有板有眼,一招一式的照着做,而那些真正的武林高手,都是“无招胜有招”的。当这些东西烂熟于心,得心应手时,对这些所谓的设计模式,都会达到一种“司空见惯”式的“视而不见”,一切都会变得水到渠成,自然而然的流露。
还是我那句口头禅,既然我这个设计模式,定义为“秒懂”系类,自然是“很简单的”,我会用最直白甚至低俗的例子及图画,来给大家一一剖析。当然,可能会有些你认为是谬论的地方,觉得玷污你心中高雅的知识,请看到这里,点击右上角关闭网页。我个人始终有个观点:当高深到达了“俗不可耐”的程度,才能叫做真正的“大悟大彻”!
(一)总述
1.什么是设计模式?
【官方定义】设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。
【理解】它就是一套前人们的总结,可以理解为经验公式。
2.设计模式有什么用?
【官方定义】使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
【理解】经验公式的好处就是让你可以直接套用,大家都懂,省事还靠谱(因为前人已经验证过了)。
3.设计模式的六大原则?
①开闭原则(Open Close Principle)
【简记】对扩展开放,对修改关闭。
【理解】像插座,插孔不够用了,你可以再接一个插排(扩展),但是你不能在原插座上直接钻两个孔吧(修改)?
②里氏代换原则(Liskov Substitution Principle)
【简记】任何基类可以出现的地方,子类一定可以出现。
【理解】能用插座(基类)的地方,就一定可以用再接一个插排(子类)代替。
③依赖倒转原则(Dependence Inversion Principle)
【简记】依赖于抽象而不依赖于具体。
【理解】想用电,最终依赖的是插座(抽象)吧,不能依赖于插排(具体),万一你手头有插排,但是公寓没插座也没用啊。
④接口隔离原则(Interface Segregation Principle)
【简记】使用多个隔离的接口,比使用单个接口要好。同时降低类之间的耦合度。
【理解】不要把所有的插排,都擦在一个插座上。
⑤迪米特法则,又称最少知道原则(Demeter Principle)
【简记】一个实体应当尽量少地与其他实体之间发生相互作用,尽量相对独立。作用也是解耦合。
【理解】“达康书记”(实体)在工作及生活中,很独立,极少与亲朋好友(其它实体)互相作用,以至于妻子出事了,也没影响到自己的政治生涯(因为耦合度低)。
⑥合成复用原则(Composite Reuse Principle)
【简记】合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。
(二)工厂模式
工厂模式属于创建型模式,目的为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,提高灵活性。
工厂模式在《Java 与模式》中分为三类(从上到下越来越抽象):
①简单工厂模式(Simple Factory)[又称静态工厂方法模式]
②工厂方法模式(Factory Method)
③抽象工厂模式(Abstract Factory)
下面注意介绍:
1.简单工厂模式(又称静态工厂方法模式)
【讲故事】某思聪,富二代喜欢豪车,开始告诉手下“给我打造一辆奔驰车”,过几天嫌档次不够,又说“给我打造一辆宝马车”,结果还不知足,又说“给我打造一辆玛莎拉蒂”....周而复始,他的属下发现很麻烦,于是给他创建一个“车场”。以后,某思聪想换车,只说名字就行了。
【意图】定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类。
【Java代码】
①创建一个抽象产品角色
package com.liyan.factory; /** * 创建一个抽象产品角色 * <p>Title: Car</p> * @author Liyan * @date 2017年4月26日 下午1:55:22 */ public interface Car { /** * 抽象的创建车方法 * <p>Title: creatCar</p> * @author Liyan * @date 2017年4月26日 下午1:58:04 */ public void creatCar(); }②创建奔驰具体产品角色
package com.liyan.factory; /** * 创建奔驰具体产品角色 * <p>Title: BenzCar</p> * @author Liyan * @date 2017年4月26日 下午2:01:31 */ public class BenzCar implements Car { @Override public void creatCar() { System.out.println("奔驰车实体已经创建好了!"); } }③创建宝马具体产品角色
package com.liyan.factory; /** * 创建宝马具体产品角色 * <p>Title: BmwCar</p> * @author Liyan * @date 2017年4月26日 下午1:59:31 */ public class BmwCar implements Car { @Override public void creatCar() { System.out.println("宝马车实体已经创建好了!"); } }④创建奔驰玛莎拉蒂具体产品角色
package com.liyan.factory; /** * 创建玛莎拉蒂具体产品角色 * <p>Title: MaseratiCar</p> * @author Liyan * @date 2017年4月26日 下午2:02:44 */ public class MaseratiCar implements Car { @Override public void creatCar() { System.out.println("玛莎拉蒂车实体已经创建好了!"); } }⑤创建工厂类角色
package com.liyan.factory; /** * 创建工厂类角色 * <p>Title: CarFactory</p> * @author Liyan * @date 2017年4月26日 下午2:09:36 */ public class CarFactory { /** * <p>Title: creatCar</p> * @author Liyan * @date 2017年4月26日 下午2:09:49 * @param carName 车的名字 * @return Car 返回抽象类。想想依赖倒转原则:依赖于抽象而不依赖于具体 */ public static Car creatCar(String carName) { if (carName.equalsIgnoreCase("Benz")) { return new BenzCar(); } if (carName.equalsIgnoreCase("Bmw")) { return new BmwCar(); } if (carName.equalsIgnoreCase("Maserati")) { return new MaseratiCar(); } return null; } }⑥某思聪登场
package com.liyan.factory; /** * 某思聪 * <p>Title: SiCong</p> * @author Liyan * @date 2017年4月26日 下午2:12:48 */ public class SiCong { public static void main(String[] args) { //某思聪发话要创建一辆宝马车 Car car = CarFactory.creatCar("Bmw"); //车开始创建,输出是具体的宝马车 car.creatCar(); } }关系图如下:
以上便是简单工厂的例子,有没有发现很简单呢。但是,你是否发现它有些问题呢?比如,我再想创建一个产品3(创建一个劳斯莱斯车),这个时候就必须要修改CarFactory工厂角色类,那样的话是不是就不符合开闭原则(对扩展开放,对修改关闭)了呢?于是,我就有了工厂方法模式。
2.工厂方法模式
工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这
样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
【Java代码】
①创建一个抽象产品角色及具体产品角色(Car、BenzCar、BmwCar、MaseratiCar)同上。
②创建工厂角色
package com.liyan.factorymethod; /** * 2号工厂,演示工厂方法模式 * <p>Title: CarFactory2</p> * @author Liyan * @date 2017年4月26日 下午3:24:31 */ public interface CarFactory { public Car creatCar(); }③创建奔驰分厂角色
package com.liyan.factorymethod; /** * 创建奔驰的分厂 * <p>Title: BenzCarFactory</p> * @author Liyan * @date 2017年4月26日 下午3:37:58 */ public class BenzCarFactory implements CarFactory { @Override public Car creatCar() { return new BenzCar(); } }④创建宝马分厂角色
package com.liyan.factorymethod; /** * 创建宝马的分厂 * <p>Title: BenzCarFactory</p> * @author Liyan * @date 2017年4月26日 下午3:38:02 */ public class BmwCarFactory implements CarFactory { @Override public Car creatCar() { return new BmwCar(); } }⑤创建玛莎拉蒂的分厂角色
package com.liyan.factorymethod; /** * 创建玛莎拉蒂的分厂 * <p>Title: MaseratiCarFactory</p> * @author Liyan * @date 2017年4月26日 下午3:40:51 */ public class MaseratiCarFactory implements CarFactory { @Override public Car creatCar() { return new MaseratiCar(); } }⑥某思聪登场
package com.liyan.factorymethod; /** * 某思聪 * <p>Title: SiCong</p> * @author Liyan * @date 2017年4月26日 下午2:12:48 */ public class SiCong { public static void main(String[] args) { //把创建对象的压力分散到具体的分厂 CarFactory carFactory = new MaseratiCarFactory(); //分厂创建玛莎拉蒂 Car car = carFactory.creatCar(); car.creatCar(); } }关系图如下:
这就是工厂方法模式。为每一个产品,单独创建一个分厂。这样就符合了开闭原则。但是,依然存在弊端:可以看出工厂方法的加入,使得对象的数量成倍增长,当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。
所以,它的适用场景是:
1) 当客户程序不需要知道要使用对象的创建过程。
2) 客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象。
细心的人可能发现,其实工厂模式并没有真正的解决代码的改动问题。比如简单工厂,新增产品要修改工厂类;工厂方法模式,新增产品要创建新工厂。面对这样的瓶颈问题,难道真的没办法解决了吗?
答案肯定是否定的。仅做一个小提示,关键字:Spring、配置文件、反射。
3.抽象工厂模式
抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
(解释一下什么叫产品族?简单理解,一系列同类型产品的集合。比如奔驰,下面又可分为:奔驰汽车、奔驰跑车、奔驰商务车、奔驰SUV等,这些分支及以下的产品,都可以统称为产品族)
【Java代码】
①创建车身抽象产品角色及具体产品角色(Car、BenzCar、BmwCar、MaseratiCar)同上。
②创建车类型抽象产品角色及具体产品角色
package com.liyan.abstractfactory; /** * 车的类型抽象产品类型 * <p>Title: CarType</p> * @author Liyan * @date 2017年4月26日 下午4:41:05 */ public interface CarType { /** * 创建车的类型 * <p>Title: createCarType</p> * @author Liyan * @date 2017年4月26日 下午4:42:42 * void */ public void createCarType(); }商务类型产品角色
package com.liyan.abstractfactory; /** * 商务类型 * <p>Title: BusinessCarType</p> * @author Liyan * @date 2017年4月26日 下午4:43:54 */ public class BusinessCarType implements CarType{ @Override public void createCarType() { System.out.println("我是商务类型!"); } }SUV类型产品角色
package com.liyan.abstractfactory; /** * SUV类型 * <p>Title: BusinessCarType</p> * @author Liyan * @date 2017年4月26日 下午4:43:54 */ public class SuvCarType implements CarType{ @Override public void createCarType() { System.out.println("我是SUV类型!"); } }跑车类型产品角色
package com.liyan.abstractfactory; /** * 跑车类型 * <p>Title: BusinessCarType</p> * @author Liyan * @date 2017年4月26日 下午4:43:54 */ public class RunCarType implements CarType{ @Override public void createCarType() { System.out.println("我是跑车类型!"); } }③为车和车类型创建抽象类来获取工厂(此时为抽象类,不再是接口!)
package com.liyan.abstractfactory; /** * 3号工厂,演示抽象工厂模式(抽象类,不再是接口!) * <p>Title: CarFactory2</p> * @author Liyan * @date 2017年4月26日 下午3:24:31 */ public abstract class Factory { /** * 创建车主体 * <p>Title: creatCar</p> * @author Liyan * @date 2017年4月26日 下午4:47:51 * @return Car */ abstract Car creatCar(String carName); /** * 创建车的类型 * <p>Title: creatCarType</p> * @author Liyan * @date 2017年4月26日 下午4:48:01 * @return CarType */ abstract CarType creatCarType(String carType); }④创建车体子类工厂
package com.liyan.abstractfactory; import com.liyan.abstractfactory.BenzCar; import com.liyan.abstractfactory.BmwCar; import com.liyan.abstractfactory.MaseratiCar; /** * 创建车体子类工厂 * <p>Title: CarFactory</p> * @author Liyan * @date 2017年4月26日 下午4:56:14 */ public class CarFactory extends Factory { @Override Car creatCar(String carName) { if (carName.equalsIgnoreCase("Benz")) { return new BenzCar(); } if (carName.equalsIgnoreCase("Bmw")) { return new BmwCar(); } if (carName.equalsIgnoreCase("Maserati")) { return new MaseratiCar(); } return null; } @Override CarType creatCarType(String carType) { return null; } }⑤创建车类型子类工厂
package com.liyan.abstractfactory; /** * 创建车类型子类工厂 * <p>Title: CarTypeFactory</p> * @author Liyan * @date 2017年4月26日 下午5:04:57 */ public class CarTypeFactory extends Factory { @Override Car creatCar(String carName) { return null; } @Override CarType creatCarType(String carType) { if (carType.equalsIgnoreCase("Business")) { return new BusinessCarType(); } if (carType.equalsIgnoreCase("Suv")) { return new SuvCarType(); } if (carType.equalsIgnoreCase("Run")) { return new RunCarType(); } return null; } }⑥创建一个工厂创造器/生成器类,通过传递车品牌或车类型信息来获取工厂。
package com.liyan.abstractfactory; /** * 工厂创造器/生成器类 * <p>Title: FactoryProducer</p> * @author Liyan * @date 2017年4月26日 下午5:08:11 */ public class FactoryProducer { public static Factory getFactory(String key) { if (key.equalsIgnoreCase("Car")) { return new CarFactory(); } if (key.equalsIgnoreCase("CarType")) { return new CarTypeFactory(); } return null; } }⑦我们的主角思聪登场
package com.liyan.abstractfactory; /** * 某思聪 * <p>Title: SiCong</p> * @author Liyan * @date 2017年4月26日 下午2:12:48 */ public class SiCong { public static void main(String[] args) { //某思聪此时想要一辆宝马款的商务车 //1.通过工厂触发器,获取创建车体的工厂 Factory carFactory = FactoryProducer.getFactory("Car"); Car car = carFactory.creatCar("Bmw"); car.creatCar(); //2.通过工厂触发器,获取创建车类型的工厂 Factory typeFactory = FactoryProducer.getFactory("CarType"); CarType carType = typeFactory.creatCarType("Business"); carType.createCarType(); } }关系图如下:
总结(简单工厂,工厂方法,抽象工厂三者的异同):
(1)相同点:
都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。
(2)不同点:
①简单工厂模式:一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。不满足开闭原则(不修改代码的话,是无法扩展的)。
②工厂方法模式:针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。在同一等级结构中,支持增加任意产品。满足开闭原则。
③抽象工厂模式:应对产品族概念而生,增加新的产品线很容易,但是无法增加新的产品。