【设计模式】简单工厂、工厂方法和抽象工厂

时间:2022-10-02 18:24:36

工厂模式是创建型模型,提供了一种创建对象的方式,工厂模式又分为简单工厂工厂方法抽象工厂

简单工厂

简单工厂模式,又称静态工厂方法,通过一个工厂类,根据不同的参数创建其他类的实例,
屏蔽了每个类的创建细节,只暴露出工厂的创建方法,假如现在要实现一个简单计算器
实现两个数的加减乘除

抽象出运算类

public abstract class Operation {
public double numberA;

public double numberB;
abstract double getResult();
}

加、减、乘类分别继承运算类

public class OperationAdd extends Operation {

@Override
double getResult() {
return numberA + numberB;
}
}

public class OperationSub extends Operation {

@Override
double getResult() {
return numberA - numberB;
}
}

public class OperationMul extends Operation {
@Override
double getResult() {
return numberA*numberB;
}
}

工厂类

public class OperationFactory {
public static Operation createOperation(String opt){
Operation operation = null;
switch (opt){
case "+" :
return new OperationAdd();
case "-":
return new OperationSub();
case "*":
return new OperationMul();
default:
return null;
}
}
}

客户端

public class TestFactory {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.print("a:");
double a = sc.nextDouble();
System.out.print("operation:");
String opt = sc.next();
System.out.print("b:");
double b = sc.nextDouble();
Operation operation = OperationFactory.createOperation(opt);
operation.numberA = a;
operation.numberB = b;
System.out.println(operation.getResult());
}
}

在客户端,当我们想要计算两个数相加的时候,只需要输入+号,工厂类就会自动的帮我们实例化一个加法的运算类,而不需要自己去想着如何实例化一个加法类,如果需求要求我们给计算器增加除法运算的时候,只需要新增一个除法类

public class OperationDiv extends Operation {
@Override
double getResult() {
return numberA/numberB;
}
}

同时在工厂类createOperation的switch分支增加除法的创建就可以了

public class OperationFactory {
public static Operation createOperation(String opt){
Operation operation = null;
switch (opt){
case "+" :
return new OperationAdd();
case "-":
return new OperationSub();
case "*":
return new OperationMul();
case "/":
return new OperationDiv();
default:
return null;
}
}
}

简单工厂实现计算器的类图

【设计模式】简单工厂、工厂方法和抽象工厂

工厂方法

简单工厂模式的优点是工厂中包含了必要的逻辑判断根据客户端的选择条件动态的实例化相关的类,对于客户端来说,去除了与具体的产品的依赖

比如上边所说的计算器,客户端不去管该实例化哪个类,它只要输入相对应的运算符号,工厂类自动的实例化出对应的运算类,当需要新增一种运算时,之前也说了,需要新增一种运算类,并修改工厂里的switch分支,这违背了开放-封闭原则,于是工厂方法

开放-封闭原则

软件实体(类,模块,函数等)应该是可以扩展的,但是不可以修改

工厂方法类图

【设计模式】简单工厂、工厂方法和抽象工厂

先构建一个工厂的接口

public interface IFactory {
Operation createOperation();
}

加减乘除各建一个具体的工厂去实现接口

public class AddFactory implements IFactory {
@Override
public Operation createOperation() {
return new OperationAdd();
}
}

public class SubFactory implements IFactory {
@Override
public Operation createOperation() {
return new OperationSub();
}
}

public class MulFactory implements IFactory {
@Override
public Operation createOperation() {
return new OperationMul();
}
}

public class DivFactory implements IFactory {
@Override
public Operation createOperation() {
return new OperationDiv();
}
}

客户端

public class TestFactory {
public static void main(String[] args){
IFactory operFactory = new AddFactory();
Operation operation = operFactory.createOperation();
operation.numberA = 2;
operation.numberB = 3;
System.out.println(operation.getResult());
}
}

这样当我们要增加一种运算时,只需要增加一种运算类和对应的工厂,而不必去修改原来的方法和类,就是所谓的对修改封闭,对扩展开放,工厂方法在对象的替换上更方便,不需要做大的改动,只需要换掉工厂就可以了,因为客户端并不知道他持有的具体是哪一个产品的实例,是由对应的工厂决定的

抽象工厂

抽象工厂模式,提供了一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。

有这样一个场景,某个产品使用的是sqlserver数据库,数据库中有两张表,用户表user和部门表department,分别可以插入数据和查询数据,现在由于使用者用的是Access数据库,由于Access数据库的语法和sqlserver有很大的出入,原来的数据库操作代码不能使用,这个时候我们可能会一条一条的去改数据库操作语句,但是用抽象工厂就可以很方便的解决

public class User {
private int id;
private String name;
}

public class Department {
int id;
int name;
}

抽象出IUser和IDepartment接口,与数据库操作解耦

public interface IUser {
void insert(User user);
User getUser(int id);
}

public interface IDepartment {
void insert(Department department);
IDepartment getDepartment(int id);
}

每种数据库自己的数据库操作,实现对应的数据库操作接口

public class SqlserverUser implements IUser {

@Override
public void insert(User user) {
System.out.println("sql server 中 user 表插入了一条记录");
}

@Override
public User getUser(int id) {
System.out.println("sql server 中获取了 user 表的一条记录");
return null;
}
}

public class SqlserverDepartment implements IDepartment {

@Override
public void insert(Department department) {
System.out.println("sql server 中 Department 表插入了一条记录");
}

@Override
public IDepartment getDepartment(int id) {
System.out.println("sql server 中获取了 Department 表的一条记录");
return null;
}
}


public class AccessUser implements IUser {
@Override
public void insert(User user) {
System.out.println("Access 中 user 表插入了一条记录");
}

@Override
public User getUser(int id) {
System.out.println("Access 中获取了 user 表的一条记录");
return null;
}
}

public class AccessDepartment implements IDepartment {
@Override
public void insert(Department department) {
System.out.println("Access 中 Department 表插入了一条记录");
}

@Override
public IDepartment getDepartment(int id) {
System.out.println("Access 中获取了 Department 表的一条记录");
return null;
}
}

具体数据库操作的抽象工厂IFactory

public interface IFactory {
IUser createUser();
IDepartment createDepartment();
}

SqlserverFactory类实现IFactory接口,实例化SqlserverUser和SqlserverDepartment

public class SqlserverFactory implements IFactory {
@Override
public IUser createUser() {
return new SqlserverUser();
}

@Override
public IDepartment createDepartment() {
return new SqlserverDepartment();
}
}

AccessFactory类实现IFactory接口,实例化AccessUser和AccessDepartment

public class AccessFactory implements IFactory {
@Override
public IUser createUser() {
return new AccessUser();
}

@Override
public IDepartment createDepartment() {
return new AccessDepartment();
}
}

客户端代码,当切换数据库的时候只需要更改相应的数据库工厂就可以了

public class TestAbsFactory {
public static void main(String[] args) {
// IFactory factory = new AccessFactory();
IFactory factory = new SqlserverFactory();
IUser iUser = factory.createUser();
IDepartment iDepartment = factory.createDepartment();

iUser.insert(new User());
iUser.getUser(1);

iDepartment.insert(new Department());
iDepartment.getDepartment(1);

}
}

【设计模式】简单工厂、工厂方法和抽象工厂

当有多个抽象的产品时使用抽象的工厂模式,如图中有两个抽象产品AbstractProductA和AbstractProductB,对应例子中的IUser和IDepartment,这两种抽象产品有着完全不同的产品实现,比如IUser有AccessUser和SqlserverUser两种不同的实现,AbstractFactoty是一个抽象的工厂,包含了所有产品创建的抽象方法,ConcreteFactory1和ConcreteFactory2具体的工厂就是SqlserverFactory和AccessFactory了,这样client使用不同的具体工厂,就可以创建出不同的产品对象啦