zoukankan      html  css  js  c++  java
  • 大话设计模式读书笔记(抽象工厂模式)

    人物:大鸟,小菜

    事件:一天晚上小菜回来得很晚,大鸟问原因,原来小菜是因为做完一个项目,项目以SQL Server为基础,但后面要改成Access,换数据库用时很久导致了加班很晚才回来,大鸟告诉小菜用抽象工厂模式可以帮他在下一次还要换数据库时,节省很多时间


    抽象工厂模式:

    1.先实现最基本的数据访问方式,阐述不足,引出改进点

    2.接着用工厂方法模式进行改进,阐述剩下的不足,引出抽象工厂模式

    3.介绍抽象工厂模式

    4.结合抽象工厂模式实现案例

    5.阐述抽象工厂模式优劣

    需求:大鸟让小菜先实现最近本的数据访问

    最近本的数据访问

    用户类

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

    SqlServerUser类,用于操作User表(这里只有新增和选取一条记录的功能)

    @Slf4j
    public class SqlServerUser {
        public void insert(User user) {
            log.info("在SQL Server中给User表增加一条记录");
        }
    
        public User getUser(int id) {
            log.info("在SQL Server中根据id在User表得到一条记录");
            return null;
        }
    }

    客户端代码:

    public static void main(String[] args) {
        User user = new User();
        SqlServerUser su = new SqlServerUser();
        su.insert(user);
        su.getUser(1);
    }

    小菜:这就是我最开始写的,很简单。

    大鸟:之所以不能换数据库,是因为SqlServerUser su = new SqlServerUser();这里使得su这个对象被框死在了Sql Server上,如果是灵活的,多态的,那么在执行su.insert()语句时,就不用考虑是在Sql Server还是Access上了 

    小菜:那我可以使用工厂方法模式,因为工厂方法模式是定义用来创建对象的接口,让子类决定实例化哪一个类,用工厂方法模式来封装new SqlServer所造成的变化

    用了工厂方法模式的数据访问

    代码结构图

    UserService接口,用于客户端访问,解除与具体数据库访问的耦合:

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

    SqlServerUser类,用于访问SQL Server的user:

    @Slf4j
    public class SqlServerUser implements UserService {
        @Override
        public void insert(User user) {
            log.info("在SQL Server中给User表增加一条记录");
        }
    
        @Override
        public User getUser(int id) {
            log.info("在SQL Server中根据id在User表得到一条记录");
            return null;
        }
    }

    AccessUser类,用于访问Access的user:

    @Slf4j
    public class AccessUser implements UserService {
        @Override
        public void insert(User user) {
            log.info("在Access中给User表增加一条记录");
        }
    
        @Override
        public User getUser(int id) {
            log.info("在Access中根据id在User表得到一条记录");
            return null;
        }
    }

    Factory接口,定义一个创建访问User表对象的抽象的工厂接口:

    public interface Factory {
        UserService createUser();
    }

    SqlServerFactory类,实现Factory接口,实例化SQLServerUser:

    public class SqlServerFactory implements Factory {
        @Override
        public UserService createUser() {
            return new SqlServerUser();
        }
    }

    AccessFactory类,实现Factory类,实例化AccessUser:

    public class AccessFactory implements Factory {
        @Override
        public UserService createUser() {
            return new AccessUser();
        }
    }

    客户端代码:

    public static void main(String[] args) {
        User user = new User();
        Factory factory = new SqlServerFactory();
        UserService iu = factory.createUser();
        iu.insert(user);
        iu.getUser(1);
    }

    大鸟:很好,只有User类和User操作类时,是只需要工厂方法模式的,但显然数据库中有很多表,又该怎么做呢?我来介绍下抽象工厂模式就知道了

    抽象工厂模式

    1.概念:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类

    2.代码结构图:

    其中:

    (1)AbstractProductA和AbstractProductB是两个抽象产品,之所以抽象是因为它们都有可能有两种不同的实现

    (2)ProductA1,ProductA2,ProductB1,ProductB2都是对抽象产品具体分类的实现()ProductA1,ProductA2可以分别理解为:SqlServerUser和AccessUser)

    (3)Factory是一个抽象工厂的接口,里面应该包含所有产品创建的抽象方法

    (4)而ConcreteFactory1和ConcreteFactory2就是具体的工厂了(例如:SqlServerFactory和AccessFactory)

    结合抽象工厂模式进行数据访问

    IDepartment接口,用于客户端的访问,解除与具体数据库访问的耦合:

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

    Department类:

    @Data
    public class Department {
        private int id;
        private String name;
    }

    SqlServerDepartment类,用于访问SQLServer:

    @Slf4j
    public class SqlServerDepartment implements IDepartment {
        @Override
        public void insert(Department department) {
            log.info("在SQL Server中给Department表增加一条记录");
        }
    
        @Override
        public Department getDepartment(int id) {
            log.info("在SQL Server中根据id得到Department表一条记录");
            return null;
        }
    }

    AccessDepartment类,用于访问Access:

    @Slf4j
    public class AccessDepartment implements IDepartment {
        @Override
        public void insert(Department department) {
            log.info("在Access中给Department表增加一条记录");
        }
    
        @Override
        public Department getDepartment(int id) {
            log.info("在Access中根据id得到Department表一条记录");
            return null;
        }
    }

    IFactory接口,用于定义一个创建访问User表对象的抽象的工厂接口:

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

    SQLServerFactory类,用于实现IFactory接口,实例化SQLServerUser和SQLServerDepartment:

    public class SqlServerFactory implements IFactory {
        @Override
        public UserService createUser() {
            return new SqlServerUser();
        }
    
        @Override
        public IDepartment createDepartment() {
            return new SqlServerDepartment();
        }
    }

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

    public class AccessFactory implements IFactory {
        @Override
        public UserService createUser() {
            return new AccessUser();
        }
    
        @Override
        public IDepartment createDepartment() {
            return new AccessDepartment();
        }
    }

    客户端代码:

    public static void main(String[] args) {
        User user = new User();
        Department dept = new Department();
        IFactory factory = new AccessFactory();
        UserService iu = factory.createUser();
        iu.insert(user);
        iu.getUser(1);
        IDepartment id = factory.createDepartment();
        id.insert(dept);
        id.getDepartment(1);
    }

    结果显示如下:

    在Access中给User表增加一条记录
    在Access中根据id在User表得到一条记录
    在Access中给Department表增加一条记录
    在Access中根据id得到Department表一条记录

    抽象工厂模式的优缺点 

    优点:

    1.易于交换产品系列,由于工厂类在一个应用中只需要在初始化的时候出现过一次(如:IFactory factory = new AccessFactory()),这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置

    2.它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中

    缺点:

    1.虽然切换数据库很方便,但如果要加新的功能,比如加一个项目表Project,这时至少要增加IProject,SqlServerProject,AccessProject,还需要改IFactory,SqlServerFactory,AccessFactory才能实现,很麻烦

    2.还有就是虽然IFactory factory = new AccessFactory()这样声明一次很容易改,但如果有100个地方用到,那不是要一个一个改?

    用简单工厂改进抽象工厂

    代码结构图:

    DataAccess类:

    public class DataAccess {
        private static String db = "SqlServer";
        //private static String db = "Access";
    
        public static UserService createUser() {
            UserService result = null;
            switch (db) {
                case "SqlServer":
                    result = new SqlServerUser();
                    break;
                case "Access":
                    result = new AccessUser();
                    break;
            }
            return result;
        }
    
        public static IDepartment createDepartment() {
            IDepartment result = null;
            switch (db) {
                case "SqlServer":
                    result = new SqlServerDepartment();
                    break;
                case "Access":
                    result = new AccessDepartment();
                    break;
            }
            return result;
        }
    }

    客户端代码:

    public static void main(String[] args) {
        User user = new User();
        Department dept = new Department();
    
        UserService iu = DataAccess.createUser();
    
        iu.insert(user);
        iu.getUser(1);
    
        IDepartment id = DataAccess.createDepartment();
        id.insert(dept);
        id.getDepartment(1);
    }

    小结:这样抛弃了IFactory,SqlServerFactory,AccessFactory三个工厂类,用DataAccess类取而代之。由于事先设置了db值(SQLServer或Access),所以在客户端只需要DataAccess.createUser()或者DataAccess.createDepartment()就行,客户端没有出现SqlServer或Access字样,从而达到了解耦的目的。

             但这样还是存在一个问题(在简单工厂模式,和简单工厂模式与工厂方法模式结合时都存在的问题),如果要新增Oracle,就需要在DataAccess中的switch case中改动了,由此为了解决这个问题引入下面内容。

    用反射+抽象工厂模式的数据访问

    暂存

    用反射+配置文件实现的数据访问

    暂存

  • 相关阅读:
    1270. 数列区间最大值(climits用法+线段树模板题)
    JDBC&DBCP总结
    1264. 动态求连续区间和(树状数组模板题)
    788. 逆序对的数量(归并排序的应用)
    归并排序(模板题)
    ZoomEye技巧
    工具或安全监测网站(不定时更新)
    bp截包
    CTF/web
    CTF/stega——图片隐写
  • 原文地址:https://www.cnblogs.com/wencheng9012/p/13426697.html
Copyright © 2011-2022 走看看