设计模式-三种工厂模式实例
1.简单工厂模式:代替new产生对象,产品的类型比较少时。
我们要获得三种不同的数据库对象,如mysql,sqlserver,oracle,它们拥有共同的特征,即可以进行抽象,简单工厂目的是将获得具体数据库实体的任务交给工厂类。
接口database:
public interface database { public void open(); public void close(); }
类mysql:
public class mysql implements database { @override public void open() { system.out.println("open mysql"); } @override public void close() { system.out.println("close mysql"); } }
类oracle:
public class oracle implements database { @override public void open() { system.out.println("open oracle"); } @override public void close() { system.out.println("close oracle"); } }
类sqlserver:
public class sqlserver implements database { @override public void open() { system.out.println("open sqlserver"); } @override public void close() { system.out.println("close sqlserver"); } }
工厂类及测试:
public class factory { public database getdatabase(string type){ if(type == null){ return null; } if(type.equalsignorecase("mysql")){ return new mysql(); } else if(type.equalsignorecase("oracle")){ return new oracle(); } else if(type.equalsignorecase("sqlserver")){ return new sqlserver(); } return null; } @test public void test(){ factory factory = new factory(); database d1= factory.getdatabase("mysql"); d1.open(); database d2= factory.getdatabase("oracle"); d2.open(); database d3= factory.getdatabase("sqlserver"); d3.open(); } }
特点: 如果要新增其他数据库,只需创建新数据库类实现功能接口,修改工厂类。
问题1:根据“开闭原则”:对现有功能进行拓展,但不允许修改原有代码。很明显,这时简单工厂模式需多次修改工厂类。
问题2:使用者实际使用时并不知道类名,他只知道有database这个接口,使用这个接口就能创建对象,使用open()函数,当然实际中肯定还需传入用户名和密码等参数。
针对问题2:可以使用枚举的方式,代码如下:
public class factory2 { enum databasetype{ mysql, sqlserver, oracle } public static database getdatabase(databasetype type){ switch(type){ case mysql: return new mysql(); case oracle: return new oracle(); case sqlserver: return new sqlserver(); default: throw new unknowntypeexception(null, type); } } @test public void test(){ database s1=factory2.getdatabase(databasetype.mysql); s1.open(); database s2=factory2.getdatabase(databasetype.oracle); s2.open(); } }
2.工厂方法模式(factory method):定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实例化延迟到其子类。
使用简单工厂方式使得工厂函数很难维护,而且请求者还需知道被实例化的类,而工厂方法模式可避免之。
新建抽象工厂 ifactory,对于每个数据库都新建相应的工厂类,如mysqlfactory,oraclefactory实现 ifactory,这样在新增一种数据库时,不必修改工厂类而造成破坏封闭原则。
public interface ifactory { public database get(); }
class sqlfactory implements ifactory{ public database get(){ return new mysql(); } } class oraclefactory implements ifactory{ public database get(){ return new oracle(); } } class sqlserverfactory implements ifactory{ public database get(){ return new sqlserver(); } } //测试 public class factorymethod{ public static void main(string[] args){ database d1=new sqlfactory().get(); d1.open(); } }
问题1:如果数据库类型越来越多,将无限的增加工厂子类,使用者同样还是需要知道工厂子类的名称。
问题2:在使用时数据库时可能还需与其他的类相关联,工厂方法模式无法解决。
3.抽象工厂模式(abstract factory):提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。
场景:若在新建数据库连接时需指定方言,而且不同数据库的sql语言也不相同的情况下。
组成元素:
1.方言类和接口,如图所示:
2.之前的数据库类和接口
3.operfactory抽象工厂
public abstract class operfactory{ private idialect dialect; abstract void add(); abstract void delete(); abstract void update(); public void setdialect(string classname){ idialect cf=null; try { cf=(idialect)class.forname(classname).newinstance(); setdialect(cf); } catch (instantiationexception | illegalaccessexception | classnotfoundexception e) { e.printstacktrace(); } } public idialect getdialect() { return dialect; } public void setdialect(idialect dialect) { this.dialect = dialect; } }
4. mysqlfatory,oraclefactory( 均继承上面的抽象工厂类)
public class mysqlfatory extends operfactory { @override void add() { system.out.println("使用的方言为:"+this.getdialect().showdialect()); system.out.println("mysql add()"); } @override void delete() { system.out.println("使用的方言为:"+this.getdialect().showdialect()); system.out.println("mysql delete()"); } @override void update() { system.out.println("使用的方言为:"+this.getdialect().showdialect()); system.out.println("mysql update()"); } } public class oraclefactory extends operfactory { @override void add() { system.out.println("使用的方言为:"+this.getdialect().showdialect()); system.out.println("oracle add()"); } @override void delete() { system.out.println("使用的方言为:"+this.getdialect().showdialect()); system.out.println("oracle delete()"); } @override void update() { system.out.println("使用的方言为:"+this.getdialect().showdialect()); system.out.println("oracle update()"); } }
5.工厂构造器类 factoryproducer 负责生成想要的数据库工厂。
public class factoryproducer { public static operfactory getfactory(string class_name) { operfactory cf=null; try { cf=(operfactory)class.forname(class_name).newinstance(); } catch (instantiationexception | illegalaccessexception | classnotfoundexception e) { e.printstacktrace(); } return cf; } }
6.测试
public static void main(string[] args){ operfactory of2=factoryproducer.getfactory(databasefactorytype.mysql); of2.setdialect(dialecttype.mysql5innodbdialect); of2.update(); operfactory of= factoryproducer.getfactory(databasefactorytype.oralce); of.setdialect(dialecttype.oracle10gdialect); of.add(); }
结果:
总之抽象工厂总是关于创建一系列相关或相互有依赖的对象,以上例子中即database对象与dialect对象具有相关性。但是使用者要了解各种工厂和方言。
三者区别:
简单工厂实现简单,扩展也很容易,但是会频繁修改工厂类代码,难以维护,在维护的时候容易引发新的bug。
工厂方法模式则是把对象的实例化延迟到了继承的子类里面,这样就变成了扩展工厂,从而满足了“开闭”原则, 但是不支持产品切换,也就是只能满足一层的产品(算法)抽象,当需与其他对象合作时无能为力。
抽象工厂则是继续把产品进行再次抽象,最后得到一个可以支持产品切换的结构,但问题过于复杂,不过我们使用反射机制,可以弥补这个缺点,但是使用者却需要知道工厂的名称。
以上工程源码分享:链接:https://pan.baidu.com/s/1elhalizkrr1n7391jw8uta 密码:1ikz