工厂方法模式:定义一个用于创建对象的接口,但是让子类决定将哪个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
这个模式还是比较好理解,且使用场景比较频繁。简单工厂是只有一个工厂类,通过if else 根据不同的参数返回对应的对象;而工厂方法去除了if else 的判断,更加的灵活,它把返回对象的功能交给了子类(而简单工厂是将返回对象的功能交给了一个固定的工厂类),通过扩展子类来实现不同的功能。
工厂方法的类图如下:
Product(抽象产品):定义产品的接口,具体产品对象的父类。
ConcreateProduct(具体产品):实现了抽象产品类的方法,它与具体的工厂一一对应。
Factory(抽象工厂):它声明了抽象的方法,用来返回一个产品,所有具体的工厂都要实现该接口。
ConcreateFactory(具体工厂):它是抽象工厂的子类,返回具体的产品,与具体产品类一一对应。
我们可以实现一个简单的计算器,拥有加减乘除四个算法,每个算法对应一个工厂类。
产品的抽象类,即抽象的操作类
public abstract class Operation {
private Integer a;
private Integer b;
abstract Integer operation(Integer a, Integer b);
}
具体的加减乘除类四个类,继承父类Operation。
public class AddOperation extends Operation {
@Override
Integer operation(Integer a, Integer b) {
return a + b;
}
}
public class SubOperation extends Operation {
@Override
Integer operation(Integer a, Integer b) {
return a - b;
}
}
public class MultiOperation extends Operation {
@Override
Integer operation(Integer a, Integer b) {
return a * b;
}
}
public class DivOperation extends Operation {
@Override
Integer operation(Integer a, Integer b) {
return a / b;
}
}
抽象的工厂,四个工厂的接口,定义了抽象的计算方法。
public interface OperationFactory {
//返回抽象的产品,子类返回具体的产品---里氏替换
Operation createOperation();
}
四个具体的工厂类
public class AddFactory implements OperationFactory {
@Override
public Operation createOperation() {
return new AddOperation();
}
}
public class SubFactory implements OperationFactory {
@Override
public Operation createOperation() {
return new SubOperation();
}
}
public class MultiFactory implements OperationFactory {
@Override
public Operation createOperation() {
return new MultiOperation();
}
}
public class DivFactory implements OperationFactory {
@Override
public Operation createOperation() {
return new DivOperation();
}
}
客户端
public class Client {
public static void main(String[] args) {
//处理加法
OperationFactory addFactory= new AddFactory();
Operation operation = addFactory.createOperation();
System.out.println(operation.operation(1, 2));
//处理减法
OperationFactory subFactory= new SubFactory();
Operation operation1 = subFactory.createOperation();
System.out.println(operation1.operation(1, 2));
// 乘法、除法同上
}
}
如果继续扩展的话,我们不用改动原来的代码,只需要再增加一个产品对象和对应的产品工厂就好了,非常符合开闭原则。但是如果有100个算法,我们就要创建100个具体产品类和100个具体产品工厂,维护起来太难了。如果我们用jdk8或者以上版本的话,可以通过lamda进行简化,不需要创建具体的类了,直接通过参数行为化来搞。
思路是这样:因为加减乘除这四个算法的入参和返回值的类型都是一样的(入参都是两个数,返回值都是一个数),这样就可以搞一个函数式接口,里面的抽象方法入参是两个数,返回值是一个数。然后将加减乘除四个算法的具体实现写在外面进行参数行为化。实现如下:
计算的函数式接口,入参数是T类型,返回值是V类型,如果只用来进行数的计算,可以直接写成Integer类型。
public interface Operate<T, V> {
V operation(T a, T b);
}
客户端直接搞。
public class Client {
static Map<String, Operate<Integer, Integer>> map = new HashMap<>();
static {
map.put("add", (a, b) -> {
return (a + b);
});
map.put("sub", (a, b) -> {
return (a - b);
});
}
public static void main(String[] args) {
//执行加法
Integer add = map.get("add").operation(1, 2);
System.out.println(add);
//执行减法
Integer sub = map.get("sub").operation(1, 2);
System.out.println(sub);
}
}
通过lambda,可以将计算的实现写在map的value里面。可以将map单独放在一个类里进行维护,当有新的计算操作时,就只维护这一个map的类就可以了(只需要执行put方法,但从一定程度上不符合开闭原则)。
先说两个特别重要的概念:
里氏替换:子类型必须能够替换掉他们的基类型。
依赖倒置:高层模块不应该依赖低层模块,二者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象
工厂方法比简单工厂更符合开闭原则,当有新的需求时,不用修改之前代码。
客户端关注的是抽象的产品,具体工厂里返回的是具体的产品,这就必须让子产品永远可以替代父产品,我们维护每一个工厂类都是在管理具体的子产品。具体工厂对应抽象工厂也是同理。这就是里氏替换的应用。
客户端没有直接面向具体产品和具体工厂,而是依赖抽象产品和抽象工厂,这是高层依赖了抽象,具体产品和具体工厂实现了各自的抽象类,这是低层依赖了抽象。这是依赖倒置的应用。
最后肯定是符合开闭原则的。