作为程序员,特别是java程序员的你来说,早也听说武当山上有一种武功绝学,那就是设计模式,此绝学一共23式,招招无形胜有形,变幻莫测,若习得此绝学加成,代码如风,见招拆招,笑傲江湖!初入江湖的你,是不是已经开始搓搓手,准备上武当山了,别急,师兄我先练练你的基本功,再教你一招入门式——工厂模式。
对于设计模式,秉承“开闭原则”的总则,什么是开闭原则呢?以一个类为例,我们不可以改变它,但我们可以对它进行对应的功能扩展。好比政治上的“一国两制”,就是这个道理。在这样的总原则下,再细分为六大原则:
单一原则
不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,否则就应该把类拆分。
里氏替换原则
任何基类可以出现的地方,子类一定可以出现。里氏替换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
里氏代换原则是对“开-闭”原则的补充。实现“开闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。
依赖倒转原则
面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
接口隔离原则
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
最少知道原则
一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。
合成复用原则
尽量首先使用合成/聚合的方式,而不是使用继承。
关于上面的原则,字面意思简单又复杂,需要我们在实际的开发中慢慢积累,并加以应用。下面我们进入主题,浅析工厂模式。
简单工厂模式
朋友试想一下,long long ago,假如我需要一台宝马车,那么我们的自己造一辆,并挂个宝马的牌,ok。但是我们得工作啊,一心造车去了哪有时间,主要是哪来钱造车啊,这是宝马工厂出现了,对你说,“安心去工作吧,钱到位,一个月后提车”。这就是工厂模式了,客户端只需要给工厂特定的需求,而不需要自己去实现。下面以宝马工厂为例,用代码解析简单工厂模式。
结构图:
public abstract class Car {
//共同实现
public void canDrive(){
System.out.print("我是车,你可以开着我去泡妹子!");
}
//每个产品相同方法,不同实现
public abstract void setColor();
}
public class CarX1 extends Car {
@Override
public void setColor() {
System.out.print("红色的x1");
}
}
public class CarX5 extends Car {
@Override
public void setColor() {
System.out.print("白色的x5");
}
}
public class Creator {
public Creator() {
System.out.print("我是工厂,专注生成豪车!");
}
public Car creatCar(String type){
switch (type){
case "x1":
return new CarX1();
case "x5":
return new CarX5();
}
return null;
}
}
可以看到,我们在工厂类中,通过creatCar方法,根据不同的参数,返回不同的宝马车实例。
Creator creator = new Creator();
Car x1 = creator.creatCar("x1");
x1.setColor();
Car x2 = creator.creatCar("x2");
x2.setColor();
在客户端,通过工厂类创建不同的宝马车。
上面就是简单工厂模式了,如果只是从上面代码来看,会显得更加复杂,本来创建两个类就完事儿了,还的多余的去创建工厂类。但是试想一下,创建多个类呢,是不是上面的代码就更简洁清晰些了?采用良好的设计模式,会让项目代码变得逻辑更清晰,更具扩展性。说到扩展性,上面的代码就有问题了!当我们需要增加一类车型的时候,不仅要添加新的车型类,还的更改工厂类,这违背了开闭原则,这就要清楚工厂方法模式了。
工厂方法模式
定义:工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
关于产品类,和上面简单工厂模式里保持一致,这里先定义工厂接口:
public interface ICreator {
Car creatCar();
}
工厂类:
public class CreatorX1 implements ICreator {
@Override
public CarX1 creatCar() {
return new CarX1();
}
}
public class CreatorX5 implements ICreator {
@Override
public CarX5 creatCar() {
return new CarX5();
}
}
这里提供了两个具体的工厂类,去创建具体的产品实例。
客户端:
CreatorX1 creatorX1 = new CreatorX1();
CarX1 carX1 = creatorX1.creatCar();
carX1.setColor();
CreatorX5 creatorX5 = new CreatorX5();
CarX5 carX5 = creatorX5.creatCar();
carX5.setColor();
ok,这就是工厂方法模式了,还是比较简单有没有。不管我们需要生产多少种车型,只需要增加对应的子类就好了,聪明的你一定会想到,宝马那么多车型,那我是不是得写几十个工厂子类?大兄弟别慌,师兄我再教你一招。采用java的泛型,
创建工厂类:
public abstract class AbstractCreator {
/**
* 创建一个产品对象,其输入参数类型可以自行设置
* 通常为String、Enum、Class等,当然也可以为空
*/
public abstract T createCar(Class c);
}
public class ConcreteCreator extends AbstractCreator {
@Override
public T createCar(Class c) {
Car car = null;
try {
car = (Car)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
}
return (T)car;
}
}
在上面的工厂类加入泛型后,通过传入Car的子类,工厂类将创建对应的对象实例,
AbstractCreator creator = new ConcreteCreator();
CarX1 carX1 = creator.createCar(CarX1.class);
carX1.setColor();
CarX5 carX5 = creator.createCar(CarX5.class);
carX5.setColor();
实质上来说,工厂方法模式是对简单工厂模式的一种改良,更符合软件设计规范,可以将上面两种设计模式合并为工厂方法模式。
抽象工厂模式
有了上面的工厂方法模式,无论用户需要哪一款车型,工厂都会很乐意的进行生产并提供。随着用户生活水平的提高,生产的宝马车中,需要配置空调以及不同的发动机,X1需要A空调A发动机,X5需要B空调B发动机,你会怎么做呢?
这就需要引入我们的抽象工厂模式了。
定义:
为创建一组相关或者依赖的对象提供一个接口,而且无需指定其具体类。每个抽象产品角色都有两个或以上具体产品(产品空调有两个具体产品空调A和空调B)。抽象工厂模式提供两个或以上具体工厂角色(宝马320系列工厂和宝马230系列工厂),分别对应于这两个或以上具体产品角色,每一个具体工厂角色只负责某一个产品角色的实例化。每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。
结构图:
下面我们开始代码部分:
这里我们先定义,空调,发动机为产品族;空调A,空调B为产品等级。
发动机:
public abstract class Engine {
public void getName(){
System.out.print("我是发动机!");
}
public abstract void setType();
}
public class EngineA extends Engine {
@Override
public void setType() {
System.out.print("A 发动机--1.5T");
}
}
public class EngineB extends Engine {
@Override
public void setType() {
System.out.print("B 发动机--2.0T");
}
}
空调:
public abstract class Air {
public void getName(){
System.out.print("我是空调");
}
public abstract void setType();
}
public class AirA extends Air {
@Override
public void setType() {
System.out.print("A 空调");
}
}
public class AirB extends Air {
@Override
public void setType() {
System.out.print("B 空调");
}
}
public abstract class AbstractFactory {
//发动机
public abstract Engine creatEngine();
//空调
public abstract Air creatAir();
}
可以知道,如果设计到N个产品族,那么该抽象工厂类中,对应的也应该是N个抽象方法。
实现工厂类
宝马X1实现工厂:
public class BMWX1Factory extends AbstractFactory {
@Override
public Engine creatEngine() {
return new EngineA();
}
@Override
public Air creatAir() {
return new AirA();
}
}
宝马X5实现工厂:
public class BMWX5Factory extends AbstractFactory {
@Override
public Engine creatEngine() {
return new EngineB();
}
@Override
public Air creatAir() {
return new AirB();
}
}
若有M个产品等级,将对应M个实现工厂类。
AbstractFactory BMWX1 = new BMWX1Factory();
AbstractFactory BMWX5 = new BMWX5Factory();
EngineA engineA = (EngineA) BMWX1.creatEngine();
AirA airA = (AirA) BMWX1.creatAir();
EngineB engineB = (EngineB) BMWX5.creatEngine();
AirB airB = (AirB) BMWX5.creatAir();
ok,相信上面的代码很简单了,关于抽象工厂模式主要涉及到产品族,产品等级的概念。试想一下,如果客户又需要增加一个产品族音响,并且是三种不同的影响,那么,
小师妹,到这里,师兄已经将23式的第一式–工厂模式打给你看了,具体能参悟多少,就看你的悟性了。武学这玩意儿,就像写代码一样,除了天赋,还得实战,久经沙场,方能笑傲江湖,不是吗?