大话设计模式读书笔记(十二) 抽象工厂模式

抽象工厂模式:

书中通过小菜的公司因为新的项目需求,需要将原来的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类都不需要改动。



posted @ 2017-04-16 20:17  Will_Don  阅读(176)  评论(0编辑  收藏  举报