设计模式之简单工厂(Factory method)
1.参考文献:
http://chjavach.iteye.com/blog/800325
2.解析
在java编程中,我们要做到“面向接口编程”。下面回顾一些接口方面的知识:
(1)Java中接口的概念
在Java中接口是一种特殊的抽象类,跟一般的抽象类相比,接口里面的所有方法都是抽象方法(也因此,接口中的方法可以加abstract,也可以不加),接口里面的所有属性都是常量。也就是说,接口里面是只有方法定义而不会有任何方法实现。
(2)接口用来干什么
通常用接口来定义实现类的外观,也就是实现类的行为定义,用来约束实现类的行为。接口就相当于一份契约,根据外部应用需要的功能,约定了实现类应该要实现的功能,但是具体的实现类除了实现接口约定的功能外,还可以根据需要实现一些其它的功能,这是允许的,也就是说实现类的功能包含但不仅限于接口约束的功能。通过使用接口,可以实现不相关类的相同行为,而不需考虑这些类之间的层次关系,接口就是实现类对外的外观。
(3)接口的思想
根据接口的作用和用途,浓缩下来,接口的思想就是“封装隔离”。通常提到封装是指对数据的封装,但是这里的封装是指“对被隔离体的行为的封装”,或者是“对被隔离体的职责的封装”;而隔离指的是外部调用和内部实现,外部调用只能通过接口进行调用,而外部调用是不知道内部具体实现的,也就是说外部调用和内部实现是被接口隔离开的。
(4)使用接口的好处
由于外部调用和内部实现被接口隔离开了,那么只要接口不变,内部实现的变化就不会影响到外部应用,从而使得系统更灵活,具有更好的扩展性和可维护性,这也就是所谓“接口是系统可插拔性的保证”这句话的意思。
(5)接口和抽象类的选择
既然接口是一种特殊的抽象类,那么在开发中,何时选用接口,何时选用抽象类呢?对于它们的选择,在开发中是一个很重要的问题,特别总结两句话给大家:
- 优先选用接口
- 在如下情况应选择抽象类:既要定义子类的行为,又要为子类提供公共的功能。(可以参考博客:Java 接口和抽象类区别,里面有详细在讲述)。
3.实例
4.代码示例
package edu.sjtu.erplab.yanmo.simplefactory; public interface Api { public void print(String s); }
然后定义接口的实现Impl
package edu.sjtu.erplab.yanmo.simplefactory; public class Impl implements Api { @Override public void print(String s) { // TODO Auto-generated method stub System.out.println("this is in Impl:"+s); } }
最后定义客户端Client
package edu.sjtu.erplab.yanmo.simplefactory; public class Client { public static void main(String[] args) { //将Impl的实例赋值给Api接口类型的变量,然后客户端就可以使用Api接口类型的变量操作接口的功能。 Api api=new Impl(); api.print("abc"); } }
上述面向接口编程的例子存在一些问题:
5.简单工厂
5.1简单工厂定义:
5.2简单工厂结构:
Impl:具体实现Api的实现类,可能会有多个
Factory:工厂,选择合适的实现类来创建Api接口对象
Client:客户端,通过Factory去获取Api接口对象,然后面向Api接口编程
6.传入参数的简单工厂代码实例
Impl2.java
package edu.sjtu.erplab.yanmo.simplefactory; public class Impl2 implements Api { @Override public void print(String s) { System.out.println("this is in Impl2:"+s); } }
Factory.java
package edu.sjtu.erplab.yanmo.simplefactory; public class Factory { public static Api createApi(int type) { Api api=null; if(type==1) api=new Impl(); else if(type==2) api=new Impl2(); return api; } }
Client.java
package edu.sjtu.erplab.yanmo.simplefactory; public class Client { public static void main(String[] args) { //将Impl的实例赋值给Api接口类型的变量,然后客户端就可以使用Api接口类型的变量操作接口的功能。 // Api api=new Impl(); // api.print("abc"); //传入参数决定需要创建什么类型的Api实现 Api api=Factory.createApi(1); api.print("abc"); } }
简单工厂分析
- 来源于客户端,由Client来传入参数。上面的实例中的参数内容1跟2就是来自于客户端。
- 来源于配置文件,从配置文件获取用于判断的值。
- 来源于程序运行期的某个值,比如从缓存中获取某个运行期的值。
7.可配置的简单工厂实例
package edu.sjtu.erplab.yanmo.simplefactory; import java.io.IOException; import java.io.InputStream; import java.util.Properties; //可配置工厂 public class Factory1 { public static Api createApi(){ Properties p=new Properties();//配置文件 InputStream in=null;//输入流 try{ //Finds a resource with a given name in=Factory1.class.getResourceAsStream("FactoryTest.properties");//从此类所在的包下取资源 //Finds a resource with a given name p.load(in);//从输入流中加载配置文件 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try{ in.close(); }catch(IOException e){ e.printStackTrace(); } } //使用反射创建对象实例 Api api=null; try{ //根据配置文件中的内容来创建一个实例 api=(Api)Class.forName(p.getProperty("ImplClass")).newInstance(); }catch(InstantiationException e){ e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return api; } }
在Factory1的相同目录下创建一个配置文件,命名为:FactoryTest.properties。配置文件内容如下:
ImplClass=edu.sjtu.erplab.yanmo.simplefactory.Impl2
Client发生了一些小变换,不在需要传入参数,Client的代码实现如下:
package edu.sjtu.erplab.yanmo.simplefactory; public class Client1 { public static void main(String[] args) { Api api=Factory1.createApi(); api.print("12312"); } }