本例子背景为根据客户端不同的选择,生成不同的数据库连接
0. 无工厂代码
package nofactory; public class NoFactory { private static final short MYSQL = 0; private static final short SQLSERVER = 1; public static void connect(short dbType) { switch (dbType) { case MYSQL: System.out.println("MySQL connected"); break; case SQLSERVER: System.out.println("SQLServer connected"); break; default: System.out.println("Unknown database type"); } } public static void main(String[] args) { connect(MYSQL); } }
这种写法的坏处在于客户端代码和产品代码(数据库)紧耦合,每次需求修改(例如添加数据库,更改数据库表现方式等等)需要对整个代码进行改动。
1. 使用工厂,先看如下3个固定的产品类
DataBase.java
package db; public abstract class DataBase { public static final short MYSQL = 0; public static final short SQLSERVER = 1; public static final String MYSQL_CLASSNAME = "db.MySQL"; public static final String SQLSERVER_CLASSNAME = "db.SQLServer"; public abstract void connect(); }
MySQL.java
package db; public class MySQL extends DataBase { @Override public void connect() { System.out.println("MySQL connected"); } }
SQLServer.java
package db; public class SQLServer extends DataBase { @Override public void connect() { System.out.println("SQLServer connected"); } }
1.1 简单工厂
package simplefactory; import db.DataBase; import db.MySQL; import db.SQLServer; public class DBFactory { private DBFactory() {} public static DataBase createDataBase(short dbType) { switch (dbType) { case DataBase.MYSQL: return new MySQL(); case DataBase.SQLSERVER: return new SQLServer(); default: return null; } } }
客户端
package simplefactory; import db.DataBase; public class Test { public static void main(String[] args) { DataBase db = DBFactory.createDataBase(DataBase.MYSQL); db.connect(); } }
简单工厂的好处在于将客户端和产品分离,使得要生成不同的数据库链接只需要修改客户端就可以
缺点在于产品工厂的生产全在一起(耦合),如果添加了产品需要修改switch语句,从而会影响到其他产品
1.2 工厂方法
DBFactory.java
package factorymethod; import db.DataBase; public class DBFactory { protected DBFactory() {} protected static DataBase createDB() { return null; } }
MySQLFactory.java
package factorymethod; import db.DataBase; import db.MySQL; public class MySQLFactory extends DBFactory{ private MySQLFactory() {} public static DataBase createDB() { return new MySQL(); } }
SQLServerFactory.java
package factorymethod; import db.DataBase; import db.SQLServer; public class SQLServerFactory extends DBFactory{ private SQLServerFactory() {} public static DataBase createDB() { return new SQLServer(); } }
客户端
package factorymethod; import db.DataBase; public class Test { public static void main(String[] args) { DataBase db = SQLServerFactory.createDB(); db.connect(); } }
工厂方法的目的在于消除简单工厂里的 switch , 达到添加和修改产品类的时候产品之间互相解耦,另外要特别注意工厂父类的protected的使用,他可以防止客户端直接创造父类工厂(因为一般来说客户端和工厂类不会在同一个包里)。
其实我Y就一直明白这TM漫天遍地都是用这个形式的例子来介绍工厂方法,这个例子就是个悲剧。我就问了,你Y在客户端直接new和你用这一大堆工厂有何区别,如下:
public static void main(String[] args) { DataBase db = new MySQL(); db.connect(); }
这和你上面的工厂方法有何区别,还省去了一大堆的工厂
1.3 。。。
ABFactory.java
package abstractfactory; import db.DataBase; public class ABFactory { private ABFactory() {} public static DataBase createDB(String dbName) { try { return (DataBase) Class.forName(dbName).newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } }
客户端
package abstractfactory; import db.DataBase; public class Test { public static void main(String[] args) { DataBase db = ABFactory.createDB(DataBase.MYSQL_CLASSNAME); db.connect(); } }
他的优点就显而易见了,即使你怎么添加产品类和修改产品类,也不需要去修改工厂,而且和客户端彻底解耦,一般来讲我们可以将数据库的类名放在XML配置文件里,工厂方法去读取他就可以了,就像Spring的配置文件那样。