大话设计模式读书笔记(十二) 抽象工厂模式
抽象工厂模式:
书中通过小菜的公司因为新的项目需求,需要将原来的SQL SERVER改为Access,而引出需求。写一个数据访问(“新增用户”,“得到用户”),假设只有name和Id 两个字段。
未使用设计模式代码:
用户类
public class User { private String name; private String id; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public User(String name, String id) { super(); this.name = name; this.id = id; } }SqlServerUser 类,用于操作User表
public class SqlServerUser { public void insert(User user){ System.out.println("在Sql server插入对象"+user.getName()); } public User getUser(String id){ System.out.println("在Sql server中根据id查找一条user数据"); return null; } }
主程序:
public class Main { public static void main(String[] args) { User user= new User("小菜", "1"); SqlServerUser ss = new SqlServerUser(); ss.insert(user); ss.getUser("1"); } }而不能换数据库的原因,就在于上面代码中的SqlServerUser ss = new SqlServerUser();具体的使用哪个数据库已经写死,如果这里写的很灵活,使用多带,ss.insert()和ss.getUser(""),就不需要考虑具体是哪一个数据库了。于是引出了工厂模式。
简单工厂模式代码实现:
抽象工厂接口:
public interface IFactory { public IUser concreteIUser(); }
具体工厂类:
public class MysqlFactory implements IFactory{ @Override public IUser concreteIUser() { return new MysqlUser(); } }
public class SqlServerFactory implements IFactory{ @Override public IUser concreteIUser() { // TODO Auto-generated method stub return new SqlServerUser(); } }
IUser接口,用于数据库访问
public interface IUser { public void insert(User user); public User getUser(String id); }
public class MysqlUser implements IUser{ @Override public void insert(User user){ System.out.println("在Mysql插入对象"+user.getName()); } @Override public User getUser(String id){ System.out.println("在Mysql中根据id查找一条user数据"); return null; } } public class SqlServerUser implements IUser{ @Override public void insert(User user){ System.out.println("在Sql server插入对象"+user.getName()); } @Override public User getUser(String id){ System.out.println("在Sql server中根据id查找一条user数据"); return null; } }
public class Main { public static void main(String[] args) { User user= new User("小菜", "1"); IFactory factory = new SqlServerFactory(); IUser ss =factory.concreteIUser(); ss.insert(user); ss.getUser("1"); } }
这样,如果要更改数据库,只需要更改IFactory factory = new SqlServerFactory()这句话,改成想对应的数据库就可以了。
然而,数据库实际中不仅仅有一个User表,还会有很多其他的表,而Mysql 和Sql 又是两个不同的大类,
解决这种设计了多个产品系列的问题,有一个专门的设计模式------抽象工厂模式。
抽象工厂模式:
抽象工厂模式(Abstract Factory):它提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们具体的类。
、
抽象工厂模式的优点和缺点:
优点:1、易于交换产品系列,在一个应用中只需要在初始化的时候出现一次,这使得改变一个应用的具体工厂变得容易,它只需要改变具体工厂即可使用不同的产品配置。
2、它让具体的创建实例过程和客户端分离,客户端是通过他们的抽象接口来操纵实例,产品的具体类名也被产品的具体工厂分离,不会出现在客户端代码中。
缺点:
在新增项目是改动较大。
反射+抽象工厂模式:
通过反射,只需要将IFactory、MysqlFactory、SqlServerFactory和并成一个DataAccess类。
代码实现:
public class DataAccess {
//使用哪个数据库
public final String DATANAME ="Mysql"
//该类在哪个包下
private String packageName = "abstractFactory.reflect";
public IUser concreteIUser() throws Exception{
//通过类的地址 ,使用反射来创建对象
Class<?> z = Class.forName(packageName+"."+dataName+"User");
IUser user = (IUser) z.newInstance();
return user;
}
}
这样,在需要改动数据库时,只需要把DATANAME改为SqlServer即可。
反射+配置文件实现访问数据库:
这里我使用的是properties。
具体代码如下
public class DataAccess { //使用哪个数据库 static String dataName; //该类在哪个包下 private String packageName = "abstractFactory.reflect"; public IUser concreteIUser() throws Exception{ //通过类的地址 ,使用反射来创建对象 Class<?> z = Class.forName(packageName+"."+dataName+"User"); IUser user = (IUser) z.newInstance(); return user; } static{ Properties p = new Properties(); InputStream is = null; try { is = DataAccess.class.getClassLoader().getResourceAsStream("database.properties"); p.load(is); dataName = p.getProperty("dataName"); } catch (Exception e) { e.printStackTrace(); }finally { try { is.close(); } catch (IOException e) { } } } }
databasse.properties:
dataName=Mysql
这样,只需要在配置文件中更改dataName,就可以实现更改数据库,连DataAccess类都不需要改动。