再上一篇博客中谈到了工厂方法模式,它能将对象的创建和产品分离,同时还符合开闭原则,看起来很完美,那么它有缺陷吗?
还是接着我们的登录功能来看,在我们的系统中登录通常伴随着三张表:用户表,角色表,权限表。
那么当我们使用登录功能的时候,可能会用到三种dao:UserDao,RoleDao,PermissionDao。
这个时候我们的实现是适配MySQL的,那么我们需要
三个具体产品类:MySQLUserDao,MySQLRoleDao,MySQLPermissionDao
三个抽象工厂:UserDaoFactory,RoleDaoFactory,PermissionDao
三个具体工厂:MySQLUserDaoFactory,MySQLRoleDaoFactory,MySQLPermissionDaoFactory。
这样我们实现了对一种数据库的兼容,假如我们需要增加对Oracle的兼容:
那么我们又需要增加三个具体产品类,三个具体工厂类。
这样看来我们产品类型稍微多一点的时候,工厂方法模式会造成类的数量爆炸增长,那么如何解决这个问题呢?
这样需要今天的主角登场--抽象工厂模式
当我们看到需要多种(这里的多种是指不是同一抽象产品的实现类)产品来实现一类功能的时候,我们可以考虑将产品进行分组,上面的例子可以很明显看到,我们将MySQLUserDao,MySQLRoleDao,MySQLPermissionDao分为一组,然后将这样的一组产品用一个工厂创建,我们称这样的一组产品为一个“产品族”。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。
下面我们用代码演示:
抽象产品和他们的实现:
public interface UserDao {
void getUser();
}
class MySQLUserDao implements UserDao {
@Override
public void getUser() {
System.out.println("MySQL get user");
}
}
class OracleUserDao implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle get user");
}
}
public interface RoleDao {
void getRole();
}
class MySQLRoleDao implements RoleDao {
@Override
public void getRole() {
System.out.println("MySQL get role");
}
}
class OracleRoleDao implements RoleDao {
@Override
public void getRole() {
System.out.println("Oracle get role");
}
}
public interface PermissionDao {
void getPermission();
}
class MySQLPermissionDao implements PermissionDao {
@Override
public void getPermission() {
System.out.println("MySQL get permission");
}
}
class OraclePermissionDao implements PermissionDao {
@Override
public void getPermission() {
System.out.println("Oracle get permission");
}
}
抽象工厂和它的实现:
public interface AbstractDaoFactory {
UserDao getUserDao();
RoleDao getRoleDao();
PermissionDao getPermissionDao();
}
class MySQLDaoFactory implements AbstractDaoFactory {
@Override
public UserDao getUserDao() {
return new MySQLUserDao();
}
@Override
public RoleDao getRoleDao() {
return new MySQLRoleDao();
}
@Override
public PermissionDao getPermissionDao() {
return new MySQLPermissionDao();
}
}
class OracleDaoFactory implements AbstractDaoFactory {
@Override
public UserDao getUserDao() {
return new OracleUserDao();
}
@Override
public RoleDao getRoleDao() {
return new OracleRoleDao();
}
@Override
public PermissionDao getPermissionDao() {
return new OraclePermissionDao();
}
}
客户端的用法:
// 同样用一个配置文件动态配置
public class PropertiesUtil {
public static Object getBean(String key) {
try(InputStream inputStream = new FileInputStream("src/main/java/create/abstractFactory/application.properties")) {
Properties properties = new Properties();
properties.load(inputStream);
String property = properties.getProperty(key);
Class<?> clazz = Class.forName(property);
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
// 客户端的用法
public class Client {
public static void main(String[] args) {
AbstractDaoFactory daoFactory = (AbstractDaoFactory) PropertiesUtil.getBean("dao-factory");
daoFactory.getUserDao().getUser();
daoFactory.getRoleDao().getRole();
daoFactory.getPermissionDao().getPermission();
}
}
我们可以看到抽象工厂相较于工厂方法模式大大简化了系统的复杂度,这是显而易见的优点,但是会带来什么问题呢?试想一下,如果关于用户的操作再增加一个表,那么需要再增加一个Dao层的维护,这样一来我们的每个工厂实现和客户端的代码都需要修改,这显然是不符合开闭原则的,但是如果我们时新增一个SQLServer的实现,那么我们只需要增加一个工厂的实现类就可以了,而不用修改原来的任何代码,这样看起来它又是符合开闭原则的。我们把这种现象称为开闭原则的倾斜性。
这样看来,抽象工厂模式并不比工厂方法更优秀,它们是各有取舍,所以程序员在软件设计的时候就需要结合业务多方考量,选用最合适的模式来构建自己程序,这也是对程序员业务理解能力的考验。
原文:https://www.cnblogs.com/barneycs/p/13311246.html