本文讲解三个主要概念,简单工厂、工厂方法模式、抽象工厂模式。
首先,简单工厂也即静态工厂,当然也可以不是,但是因为我们常常把简单工厂中产生对象的方法声明为static的,所以就有了静态的由来。简单工厂的实质就是一个工厂类根据传入的参数来决定到底该创建那个产品类的实例。简单工厂的有助于实现软件的一个结构化,但是,我们也将看到,工厂类来全权决定到底创建那个类的实例这种方式会让工厂类违反“高内聚”的原则,而且一旦要加入新的产品类进来,这个时候就不得不改变工厂类的实现。
例子如下:假设我们要做一个运算器:如下几步即可完成:
第一步:实现一个产品接口:
public interface Operation {第二部:做出几个接口的实现:这里只写一个:
public double compute(double args0,double args1);
}
public class AddOperation implements Operation {第三步:传建一个工厂:根据传入的参数决定实例化那个产品对象。
@Override
public double compute(double args0,double args1) {
return args0+args1;
}
}
public class OperationFactory {
public static Operation getOpe(String ope){
if("+".equals(ope)){
return new AddOperation();
}
if("-".equals(ope)){
return new MinusOperation();
}
if("*".equals(ope)){
return new MutilOperation();
}
if("/".equals(ope)){
return new DivOperation();
}
else return null;
}
}
第四步:最后就是如何使用工厂类了:
public class TestOpe {我们从这里就很容易看到简单工厂的弊端,加入我要加入一个三角函数的操作,这个时候就不得不重新来改变我们的工厂类的实现。
private static Scanner scan = new Scanner(System.in);
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("input args0");
double a = scan.nextDouble();
System.out.println("input args1");
double b = scan.nextDouble();
System.out.println("input ope");
String ope = scan.next();
Operation operation = OperationFactory.getOpe(ope);
System.out.println("The Result is :"+ operation.compute(a,b));
}
}
|-------------------------------------------------------------------------------------------------------------
|-------------------------------------------------------------------------------------------------------------
从上面我们也看到了简单工厂的一个弊端,所以,下面我们就要使用工厂方法模式来改变它!!我们要做的工作就是讲工厂中的分支转换的if-else分支移出来,放到客户端自己去判断。
工厂方法的核心是“继承或者实现,也就是子类化”,工厂是抽象的,产品也是抽象的,同时也有具体的工厂子类和具体的产品子类实例。抽象的工厂创建抽象的产品,具体的工厂创建具体的产品。而到底要使用什么工厂,交由我们的客户端自己决定。下面我们将上面的代码变通一下。时期更加符合我们的要求:
第一步:创建抽象的工厂类:
public interface IMethodFactory {第二步:创建抽象的产品类:
public Operation createOperation();
}
public interface Operation {第三步:创建具体的工厂类:每个具体的工厂类只生成特定的产品:
public double compute(double args0,double args1);
}
public class AddMethodFactory implements IMethodFactory{第四步:创建具体的工厂类:
@Override
public Operation createOperation() {
return new AddOperation();
}
}
public class AddOperation implements Operation {第五步:客户端测试代码,这里我们假设我要们决定要使用具体的工厂是AddMethodFactory:
@Override
public double compute(double args0,double args1) {
return args0+args1;
}
}
public class TestOpe {通过以上的代码发现,如果现在需要加入一个新的计算法则,我们只需要自己实现一个具体工厂和一个具体产品,然后在我们的客户端去选择使用新的法则就可以了,而不需要改变现在已经有的代码,所以满足“修改-关闭原则”。代码结构良好。
private static Scanner scan = new Scanner(System.in);
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("input args0");
double a = scan.nextDouble();
System.out.println("input args1");
double b = scan.nextDouble();
IMethodFactory factory = new AddMethodFactory();
Operation ope = factory.createOperation();
System.out.println("The Result is :"+ ope.compute(a,b));
}
}
关于“工厂方法模式”我们还发现,在代码中,具体工厂的数量和具体产品的数量是一直的。而且我们将选择哪种工厂来创建产品的权利从工厂中转交到客户端首相,同时,客户端面向抽象编程,抽象工厂依赖抽象类,不在依赖具体的产品类,还满足依赖倒置原则。
而且这里还满足“里氏代换”原则,比如我们将客户端的代码IMethodFactory factory =- new AddMethodFactory()的子类AddMethodFactory换成其他子类,对客户端程序没有任何影响,程序的运行行为丝毫不会改变,客户端代码甚至都不知道我们的实现已经改变了。里氏代换原则可以简单的理解为一个软件实体如果使用的是一个父类,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,软件里面,把父类都替换成它的子类,程序的行为没有变化。
|------------------------------------------------------------------------------------------------------
|------------------------------------------------------------------------------------------------------
最后,我们来看看抽象工厂是什么样子的。
抽象工厂中涉及到产品族和产品类型的概念:如图:
在抽象工厂中,任何一个具体工厂都可以创建一个产品族里的所有产品,不同工厂可以生产不同的产品族,通过产品类型和所在的产品族可以唯一确定一个工厂,而且可以唯一确定一个具体的产品类。在抽象工厂中,有四个角色要着重说明一下:
抽象工厂:这个角色是这个抽象工厂模式的核心,但是他与具体的业务逻辑关系不大。
具体工厂:客户端直接调用的就是这个角色,来创建产品的实例,在这个具体工厂中,选择创建合适的一个产品族里的所有产品,这个用户我们的业务逻辑是紧密相关的。
抽象产品:这是这个模式中产品的父类和接口对象,于业务逻辑关系不大。
具体产品:具体产品实例,于业务逻辑紧密相关。
其UML图如下所示:
但是我们时刻记住:客户端一定是面向接口编程:下面我们来看一个代码实例:
第一步:首先建立我们的抽象产品类,以ProductA为例:
public interface ProductA {第二步:建立具体产品类,以ProductA_1为例:
}
public class ProductA_1 implements ProductA {第三步:建立抽象工厂类:
@Override
public String toString() {
return "this is ProductA_1";
}
}
public interface IFactory {ProductA createProductA();ProductB createProductB();}第四步:建立具体工厂类:这里以Factory_1为例:
public class Factory_1 implements IFactory {第五步:客户端测试代码:
@Override
public ProductA createProductA() {
return new ProductA_1();
}
@Override
public ProductB createProductB() {
return new ProductB_1();
}
}
public class Client {我们通过观察可以发现:利用抽象工厂方法可以在一定程度上满足“开放-关闭”原则,为什么说是一定程度上呢?我们发现了一个问题,那就是,但我们只是增加产品族的时候,只需要增加新的工厂就可以实现了,这很好的满足了这个原则,但是当我们修改的产品的类型信息是,也就是我们上面的图中,我们的改变是发生在横坐标的方向的时候,这个时候我们就必须要修改所有的工厂类代码,所以,抽象工厂还是有它的弊端的。
static ProductA productA;
static ProductB productB;
static IFactory factory;
public static void main(String[] args){
factory = new Factory_1();
productA = factory.createProductA();
productB = factory.createProductB();
System.out.println(productA +" "+productB);
}
}