本例子背景为根据客户端不同的选择,生成不同的数据库连接
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的配置文件那样。