鸣谢:《大话设计模式》
简介
是一种创建型设计模式,它提供了一种接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。抽象工厂模式允许客户端使用一个抽象的工厂接口来创建一组相关的产品对象,而不需要关心具体的产品类。
"只有一个User类和User操作类的时候,是只需要工厂方法模式的,但现
在显然你数据库中有很多的表,而SQL Server与Access又是两大不同的分
类,所以解决这种涉及多个产品系列的问题,有一个专门的工厂模式叫抽象
工厂模式。"
主要组成部分:
- 抽象工厂(Abstract Factory):抽象工厂定义了一组用于创建产品对象的抽象方法。通常每个方法对应一个产品家族(一组相关的产品)。
-
具体工厂(Concrete Factory):具体工厂是抽象工厂的实现,它负责创建特定产品家族的具体产品。
-
抽象产品(Abstract Product):抽象产品是产品家族的抽象,定义了产品的通用接口。
-
具体产品(Concrete Product):具体产品是产品家族的具体实现,它实现了抽象产品的接口。
工作流程
-
客户端通过抽象工厂接口创建一个具体工厂对象。
-
客户端使用具体工厂对象来创建一组相关的产品。
-
每个具体工厂负责创建一组相关的具体产品。
-
客户端使用这些具体产品,而不需要关心具体产品的类。
例子
抽象工厂模式结构
抽象工厂代码
抽象工厂
/**
* @author: lpy
* @Date: 2023/10/24
*/
public interface IFactory {
IUser createUser(String name);
IDepartment createDepartment(String dep);
}
具体工厂
/**
* @author: lpy
* @Date: 2023/10/24
*/
public class MysqlFactory implements IFactory{
@Override
public IUser createUser(String name) {
return new MysqlUser();
}
@Override
public IDepartment createDepartment(String dep) {
return new MysqlDepartment();
}
}
/**
* @author: lpy
* @Date: 2023/10/24
*/
public class SqlserverFactory implements IFactory{
@Override
public IUser createUser(String name) {
return new SqlserverUser();
}
@Override
public IDepartment createDepartment(String dep) {
return new SqlserverDepartment();
}
}
抽象产品
/**
* @author: lpy
* @Date: 2023/10/24
*/
public interface IUser {
void insert(User u);
User getUser(String name);
}
@Data
@AllArgsConstructor
public class Department {
private Integer id;
private String name;
private String fullPath;
}
具体产品
@Data
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private Integer age;
}
@Data
@AllArgsConstructor
public class Department {
private Integer id;
private String name;
private String fullPath;
}
/**
* @author: lpy
* @Date: 2023/10/24
*/
public class MysqlUser implements IUser {
@Override
public void insert(User u) {
System.out.println("插入:"+u.toString());
}
@Override
public User getUser(String name) {
System.out.println("getUser:"+name);
return null;
}
}
/**
* @author: lpy
* @Date: 2023/10/24
*/
public class SqlserverUser implements IUser{
@Override
public void insert(User u) {
System.out.println("插入:"+u.toString());
}
@Override
public User getUser(String name) {
System.out.println("getUser:"+name);
return null;
}
}
/**
* @author: lpy
* @Date: 2023/10/25
*/
public class MysqlDepartment implements IDepartment{
@Override
public void insert(Department dep) {
System.out.println("插入部门:"+JSON.toJSONString(dep));
}
@Override
public Department getDepartment(Integer id) {
System.out.println("查询id="+id);
return null;
}
}
/**
* @author: lpy
* @Date: 2023/10/25
*/
public class SqlserverDepartment implements IDepartment{
@Override
public void insert(Department dep) {
System.out.println("插入部门:"+JSON.toJSONString(dep));
}
@Override
public Department getDepartment(Integer id) {
System.out.println("查询id="+id);
return null;
}
}
测试类
/**
* @author: lpy
* @Date: 2023/10/24
*/
public class Test1 {
public static void main(String[] args) {
User u = new User(1, "xiaomi", 15);
IFactory ia = new SqlserverFactory();
// 切换的话,只需要这么改
IFactory im = new MysqlFactory();
IUser iu = ia.createUser("xiaox");
iu.getUser("xiaom");
iu.insert(u);
}
}
抽象工厂+简单工厂
抛 弃 了 IFactory 、 SqlserverFactory 和
AccessFactory三个工厂类,取而代之的是DataAccess类,由于事先设置了
db的值(Sqlserver或Access),所以简单工厂的方法都不需要输入参数,这
样 在 客 户 端 就 只 需 要 DataAccess.createUser ( ) 和
DataAccess.createDepartment()来生成具体的数据库访问类实例,客户端
没有出现任何一个SQL Server或Access的字样,达到了解耦的目的
/**
* @author: lpy
* @Date: 2023/10/25
*/
public class DataAccess {
private static final String name = "mysql";
public static IUser createUser(){
IUser user = null;
switch (name){
case "sqlserver":
user = new SqlserverUser();
break;
case "mysql":
user = new MysqlUser();
}
return user;
}
public static IDepartment createDepartment(){
IDepartment dep = null;
switch (name){
case "sqlserver":
dep = new SqlserverDepartment();
break;
case "mysql":
dep = new MysqlDepartment();
}
return dep;
}
}
测试类
/**
* @author: lpy
* @Date: 2023/10/25
*/
public class Test2 {
public static void main(String[] args) {
/**
* 抛 弃 了 IFactory 、 SqlserverFactory 和
* AccessFactory三个工厂类,取而代之的是DataAccess类,由于事先设置了
* db的值(Sqlserver或Access),所以简单工厂的方法都不需要输入参数,这
* 样 在 客 户 端 就 只 需 要 DataAccess.createUser ( ) 和
* DataAccess.createDepartment()来生成具体的数据库访问类实例,客户端
* 没有出现任何一个SQL Server或Access的字样,达到了解耦的目的
*/
User u = new User(2, "xb", 12);
IUser user = DataAccess.createUser();
user.getUser("bb");
user.insert(u);
}
}
抽象工厂+简单工厂+反射
/**
* @author: lpy
* @Date: 2023/10/25
*/
public class DataAccess {
private static final String assemblyName = "com.cf.sqlTest.api.designPatterns.abstractFactoryMode.baseCode.user.";
private static final String assemblyDepName = "com.cf.sqlTest.api.designPatterns.abstractFactoryMode.baseCode.department.";
private static final String db = "Sqlserver";
public static IUser createUser(){
return (IUser)getInstance(assemblyName+db+"User");
}
public static IDepartment createDepartment(){
return (IDepartment)getInstance(assemblyDepName+db+"Department");
}
public static Object getInstance(String className){
Object o = null;
try {
o = Class.forName(className).getDeclaredConstructor().newInstance();
}catch (Exception e){
System.out.println("error:\n"+e.getMessage());
o = null;
}
return o;
}
}
测试类
/**
* @author: lpy
* @Date: 2023/10/25
*/
public class AbstractReflectTest {
public static void main(String[] args) {
Department dep = new Department(1, "ac", "h/3");
IDepartment department = DataAccess.createDepartment();
department.getDepartment(1);
department.insert(dep);
}
}
应用
抽象工厂模式优缺点
- 优点:
- 封装变化:抽象工厂模式封装了对象的创建过程,使客户端与具体类的创建细节解耦,客户端代码不需要关心具体产品的类。
-
保持一致性:抽象工厂模式确保创建的产品属于同一家族,因此保持了产品之间的一致性。
-
易于替换:由于客户端代码依赖于抽象工厂接口而不是具体工厂或产品类,因此很容易替换具体工厂以创建不同的产品家族。
-
更容易扩展:添加新的产品家族(新的具体工厂和产品)相对容易,不需要修改现有代码。
-
缺点:
如果需要添加新的产品家族,就需要扩展抽象工厂和所有具体工厂的接口和实现,这可能会导致系统变得复杂。因此,抽象工厂模式适用于需要保持产品家族一致性和易于替换的情况。
7 大设计原则
不能全部遵守 , 也不能不遵守 , 注意平衡 功能 和 系统复杂度 , 找到最合适的一个点 ;
- 单一职责原则(Single Responsibility Principle - SRP):
这个原则规定一个类应该只有一个改变的理由,也就是说,一个类应该只有一个单一的职责。这有助于保持类的简单性和可维护性,因为每个类只需关注一个特定的功能。
- 开放-封闭原则(Open-Closed Principle - OCP):
开放-封闭原则要求系统中的软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改原有代码。
- 里氏替换原则(Liskov Substitution Principle - LSP):
里氏替换原则规定,子类应该能够替换其父类,而不会影响程序的正确性。也就是说,子类应该继承父类的行为,但可以扩展或修改该行为,但不应该改变父类的行为。
- 接口隔离原则(Interface Segregation Principle - ISP):
接口隔离原则要求一个类不应该强迫其客户端依赖于它们不需要的接口。应该根据客户端的需求定义小而精确的接口,而不是大而笨重的接口。
- 依赖倒置原则(Dependency Inversion Principle - DIP):
依赖倒置原则要求高级模块不应该依赖于低级模块,它们都应该依赖于抽象。具体来说,这意味着应该通过抽象接口或抽象类来定义模块之间的依赖关系,而不是直接依赖于具体实现。
- 迪米特法则(Law of Demeter - LoD):
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
- 合成复用原则(Composite Reuse Principle - CRP):
合成复用原则鼓励通过组合(合成)已有的类来实现新功能,而不是通过继承现有的类。这样可以降低系统的耦合度,并且更加灵活。
源码位置
全部源码github.com/xs-alpha/designPattern
参考文献
- 《大话设计模式》
如有内容侵权,麻烦联系博主删除
备注
/**
* ## Class类方法
* public T newInstance() 创建对象
* public String getName() 返回完整类名带包名
* public String getSimpleName() 返回类名
* public Field[] getFields() 返回类中public修饰的属性
* public Field[] getDeclaredFields() 返回类中所有的属性
* public Field getDeclaredField(String name) 根据属性名name获取指定的属性
* public native int getModifiers() 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
* public Method[] getDeclaredMethods() 返回类中所有的实例方法
* public Method getDeclaredMethod(String name, Class<?>… parameterTypes) 根据方法名name和方法形参获取指定方法
* public Constructor<?>[] getDeclaredConstructors() 返回类中所有的构造方法
* public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 根据方法形参获取指定的构造方法
* ---- ----
* public native Class<? super T> getSuperclass() 返回调用类的父类
* public Class<?>[] getInterfaces() 返回调用类实现的接口集合
* ————————————————
*
* ## Field类方法
* public String getName() 返回属性名
* public int getModifiers() 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
* public Class<?> getType() 以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
* public void set(Object obj, Object value) 设置属性值
* public Object get(Object obj) 读取属性值
*
* public void setAccessible(boolean flag) 默认false,设置为true为打破封装
* ————————————————
*
* ## Method类方法
* public String getName() 返回方法名
* public int getModifiers() 获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
* public Class<?> getReturnType() 以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
* public Class<?>[] getParameterTypes() 返回方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
* public Object invoke(Object obj, Object… args) 调用方法
* ————————————————
*
* ## Constructor类方法
* public String getName() 返回构造方法名
* public int getModifiers() 获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
* public Class<?>[] getParameterTypes() 返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
* public T newInstance(Object … initargs) 创建对象【参数为创建对象的数据】
* ————————————————
*
* 采摘自https://blog.csdn.net/qq_44715943/article/details/120587716
*/