1.首先先来看看最简单的hello world程序
public static void main(String[] args){ System.out.println("HelloWorld"); }
2.如果需要修改输出的字符串的内容,需要重新编译代码和测试相关联的功能,这是很不合理和危险的。一个更好的解决方案是让程序接收外部消息字串,这样就可以在运行时决定输出的内容。
public static void main(String[] args){ if(args.length>0) System.out.println(args[0]); else System.out.println("HelloWorld"); }
3.这样的修改可以让我们改变输出的字串,这是一个进步。但观察这个程序,有两个功能:取得外部参数、输出字串
这两个功能被捆绑在一起,这是过程化的思维方式。所以我们需要分离职责
为了使程序的架构更灵活,我们应该将这两个职责装入不同的组件里,可以使用接口来实现两个不同的职责。而主程序main只和这两个组件接口打交道。
首先建两个接口
1 public interface IMessageDisplayer { 2 /** 3 * 消息显示者接口 4 */ 5 void display(); 6 7 /** 8 * 9 * 提供get/set方法 10 */ 11 void setIMessageSupplier(IMessageSupplier s); 12 IMessageSupplier getIMessageSupplier(); 13 }
1 public interface IMessageSupplier { 2 /** 3 * 取得消息 4 */ 5 String getMessage(); 6 }
然后分别给出这两个类的实现
1 public class HelloWorldDisplayer implements IMessageDisplayer { 2 3 private IMessageSupplier messageSupplier=null; 4 5 public void display() { 6 if(null==messageSupplier){ 7 throw new RuntimeException("messageSupplier为空"); 8 } 9 System.out.println(messageSupplier.getMessage()); 10 } 11 12 public IMessageSupplier getIMessageSupplier() { 13 return messageSupplier; 14 } 15 16 public void setIMessageSupplier(IMessageSupplier s) { 17 this.messageSupplier=s; 18 } 19 }
1 public class HelloWorldSupplier implements IMessageSupplier{ 2 public String getMessage(){ 3 return "HelloWorldSup"; 4 } 5 }
然后重构下主程序即可
1 public class HelloWorldBetter { 2 /** 3 * 初步重构HelloWorld之后的主程序体 4 * 解耦了消息显示者和消息提供者这两个职责 5 */ 6 public static void main(String[] args) { 7 //创建消息显示者 8 IMessageDisplayer messageDisplayer=new HelloWorldDisplayer(); 9 //创建消息提供者 10 IMessageSupplier messageSupplier=new HelloWorldSupplier(); 11 //注入消息提供者 12 messageDisplayer.setIMessageSupplier(messageSupplier); 13 //显示消息 14 messageDisplayer.display(); 15 } 16 }
4.经过这样重构后,我们的成果已经比较成功了。但仍存在一些问题,如果想替换接口的实现类,则依然需要在程序中手动改动代码,那么程序代码依然要经历重新编译测试的风险,为了克服这一缺点,可以引入单例工厂(Singleton Factory)
我们首先创建一个单例工厂类
1 /** 2 * Message组件生产工厂,这是一个单例类 3 * 通过读取外部属性文件msgbean.properties 4 * 获取MessageDisplayer和MessageSupplier的实现类名 5 * 并由工厂创建各自的实例 6 */ 7 public class MessageSupporterFactory { 8 9 /** 10 * 全局单一的工厂实例 注意需要静态 11 */ 12 private static MessageSupporterFactory factory=null; 13 14 /** 15 * 外部属性 16 */ 17 private static Properties props; 18 19 /** 20 * 创建MessageSupportFactory单例方法 21 * 使用Synchronized保障线程安全 22 */ 23 public synchronized static MessageSupporterFactory getInstance() { 24 if(factory==null) 25 factory=new MessageSupporterFactory(); 26 return factory; 27 } 28 private MessageSupporterFactory(){ 29 props=new Properties(); 30 try{ 31 props.load(new FileInputStream("conf/ch2/msgbean.properties")); 32 }catch(Exception e){ 33 e.printStackTrace(); 34 } 35 } 36 37 /** 38 * 产生IMessageDisplayer的工厂方法 39 */ 40 public IMessageDisplayer makeMessageDisplayer(){ 41 //读取IMessagePlayer具体的实现名字 42 String displayClass=props.getProperty("display.class"); 43 try { 44 return (IMessageDisplayer)Class.forName(displayClass).newInstance(); 45 } catch (InstantiationException e) { 46 System.out.println("can not instantiate an object of type "+displayClass); 47 e.printStackTrace(); 48 } catch (IllegalAccessException e) { 49 System.out.println("can not access class "+displayClass); 50 e.printStackTrace(); 51 } catch (ClassNotFoundException e) { 52 System.out.println("can not find the class "+displayClass); 53 e.printStackTrace(); 54 } 55 return null; 56 } 57 58 /** 59 * 产生IMessageSupplier的工厂方法 60 */ 61 public IMessageSupplier makeMessageSupplier(){ 62 //读取IMessageSupplier具体的实现名字 63 String supplyClass=props.getProperty("supply.class"); 64 try { 65 return (IMessageSupplier)Class.forName(supplyClass).newInstance(); 66 } catch (InstantiationException e) { 67 System.out.println("can not instantiate an object of type "+supplyClass); 68 e.printStackTrace(); 69 } catch (IllegalAccessException e) { 70 System.out.println("can not access class "+supplyClass); 71 e.printStackTrace(); 72 } catch (ClassNotFoundException e) { 73 System.out.println("can not find the class "+supplyClass); 74 e.printStackTrace(); 75 } 76 return null; 77 } 78 }
然后只要创建properties文件即可。
display.class=ch2.HelloWorldDisplayer;
supply.class=ch2.HelloWorldSupplier;
现在只需要稍微修改一下主程序main就可以达到预期效果了。
1 public class HelloWorldBetterWithFactory { 2 public static void main(String[] args) { 3 //创建消息显示者 4 IMessageDisplayer messageDisplayer=MessageSupporterFactory.getInstance().makeMessageDisplayer(); 5 //创建消息提供者 6 IMessageSupplier messageSupplier=MessageSupporterFactory.getInstance().makeMessageSupplier(); 7 //注入消息提供者 8 messageDisplayer.setIMessageSupplier(messageSupplier); 9 //显示消息 10 messageDisplayer.display(); 11 } 12 }
程序中通过读取配置文件获得类名,并通过反射获取类的实例,从而实现输出的目的。尽管一个hello world程序不需要这么繁琐地做修改,但其中的道理却很有用: 为避免程序重新编译测试的风险,我们应该尽量把程序的责任分离,降低耦合度。而工厂模式和反射则是一个降低耦合度的很好用的方法。