为了程序更好的维护和扩展,在面向对象思维的世界里,首先是面向接口编程,然后我们应该把做什么和怎么做进行分离。
下面我将用一个开晚会的例子来演示一下,最终达到的效果是:工厂+反射+配置文件实现程序的灵活应用。会具体说明一下这个过程是怎么来的,明白了这个,就会对反射和配置文件的结合更加深刻一些。
想要实现的功能是:晚会有一个唱歌、舞蹈、小品的节目单,具体各个节目的表演者只需要一个就可以,每一个表演接口都有两个实现类(表演者),通过客户端调用不同的实现类来实现不同的节目单。表演者就是“做什么”,那么“怎么做”就是节目单了,首先来看看类图结构:
下面放代码,首先是接口,即各类表演:
<span style="font-size:14px;">/**跳舞接口*/ </span><pre name="code" class="java">public interface Dancer {
public void dance();} /**小品接口*/public interface Performer { public void performance();}/**歌唱接口*/ public interface Singer { public void sing();}
然后是各个实现类,即表演者:
Dancer接口实现类: public class YangLiPing implements Dancer { @Override public void dance() { System.out.println("杨丽萍跳舞:孔雀舞"); } } public class XiaoHuDui implements Dancer { @Override public void dance() { System.out.println("小虎队跳舞:霹雳舞"); } } Performer接口实现类: public class GongHanLin implements Performer { @Override public void performance() { System.out.println("巩汉林表演:功夫令"); } } public class ZhaoBenShan implements Performer { @Override public void performance() { System.out.println("赵本山表演:卖拐"); } } Singer接口实现类: public class ZhouHuaJian implements Singer { @Override public void sing() { System.out.println("周华健演唱:刀剑如梦"); } } public class WangFei implements Singer { @Override public void sing() { System.out.println("王菲演唱:我愿意"); } }
客户端调用方式:
<span style="font-size:14px;"> public static void main(String[] args) { //定义晚会流程 //演出:歌曲、舞蹈、表演 //第1种方法:单纯使用多态 System.out.println("晚会开始=======>>"); Singer singer = new ZhouHuaJian(); singer.sing(); Performer performer = new ZhaoBenShan(); performer.performance(); Dancer dancer = new YangLiPing(); dancer.dance(); System.out.println("<<========晚会结束"); }</span>
代码挺简单,不过发现一个问题,作为节目组织者,在现实中基本上是不应该直接与表演者打交道的,而是与其所在的公司或者代理人进行交流,假设现在所有的表演者都同属于一个娱乐公司,那么我只需要从这个娱乐公司里获得我想要的那些表演者就可以了,那么UML图将会变为如下结构(Factory就代表这个娱乐公司):
Facotry代码:
public classFactory { //提供准备歌手方法 public static Singer getSinger(){ return new ZhouHuaJian(); } //提供准备舞蹈方法 public static Dancer getDancer(){ return new YangLiPing(); } //提供准备表演方法 public static PerformergetPerformer(){ return new GongHanLin(); } }
那么客户端代码就变成了这样:
public staticvoidmain(String[] args) { //定义晚会流程 //演出:歌曲、小品、舞蹈 //第1种方法:使用工厂获取各个表演种类 System.out.println("晚会开始=======>>"); Singersinger = Factory.getSinger(); singer.sing(); Performerperformer = Factory.getPerformer(); performer.performance(); Dancerdancer = Factory.getDancer(); dancer.dance(); System.out.println("<<========晚会结束"); }
大家可以看到,在Factory代码中我们发现还是将各个实现类写死了,也就是说娱乐公司强制某些表演者必须参加,但实际上很多时候有些表演者可能没法参加,而另外一些闲着的表演者则可以参加,那么我是不是应该在需要表演者的时候动态获取有空参加更好一些呢?这个时候就需要用上配置文件+反射了。Factory代码更改如下:
<span style="font-size:14px;">public class Factory { /**提供准备歌手方法*/ public static Singer getSinger(){ //读取配置文件及获取key对应的value StringclassName = ResourceBundle.getBundle("party").getString("Singer"); Singersinger = null; try { //将配置文件中读取的完整类名通过反射获取该类的实例 singer= (Singer)Class.forName(className).newInstance(); }catch(InstantiationException | IllegalAccessException |ClassNotFoundException e) { e.printStackTrace(); } return singer; } /**提供准备舞蹈方法*/ public static Dancer getDancer(){ StringclassName = ResourceBundle.getBundle("party").getString("Dancer"); Dancerdancer = null; try { dancer= (Dancer)Class.forName(className).newInstance(); }catch(InstantiationException | IllegalAccessException |ClassNotFoundException e) { e.printStackTrace(); } return dancer; } /**提供准备表演方法*/ public static PerformergetPerformer(){ StringclassName = ResourceBundle.getBundle("party").getString("Performer"); Performerperformer = null; try { performer= (Performer)Class.forName(className).newInstance(); }catch(InstantiationException | IllegalAccessException |ClassNotFoundException e) { e.printStackTrace(); } return performer; } }</span>
配置文件party.properties中的代码:
Singer = com.lc.reflect.demo3.person.WangFei Performer = com.lc.reflect.demo3.person.ZhaoBenShan Dancer =com.lc.reflect.demo3.person.XiaoHuDui
这样就能把代码解耦了。仔细看的话,在Factory中的代码3个方法基本上没啥不同,除了变量和对象不同,发现了代码的坏味道。那就重构吧:
public classFactory1 { /**提供准备歌手方法*/ public static Singer getSinger(){ Singersinger = null; return (Singer)getObject("Singer",singer); } /**提供准备舞蹈方法*/ public static Dancer getDancer(){ Dancerdancer = null; return (Dancer)getObject("Dancer",dancer); } /**提供准备表演方法*/ public static PerformergetPerformer(){ Performerperformer = null; return (Performer)getObject("Performer",performer); } /** * 获取目标对象的方法 * @param objName 对象名称 * @param obj 对象类型 * @return对象 */ public static Object getObject(StringobjName,Object obj){ //获取配置文件中的目标对象路径 StringclassName = ResourceBundle.getBundle("party").getString(objName); try { //获取目标对象实例 obj= Class.forName(className).newInstance(); }catch(InstantiationException | IllegalAccessException |ClassNotFoundException e) { e.printStackTrace(); } return obj; } }
这样就简洁多了。
学习一块知识不仅仅要学习API是怎么用的,还要与生活相结合,做出一个个简单有效的Demo,同时在做Demo的过程中,如果发现了编写的代码有值得重构的地方,一定不要停下思考的脚步,Just do it!虽然对反射的具体实现原理和过程还不太清楚,但首先会用才能有继续研究的动力。继续努力中~~
版权声明:本文为博主原创文章,未经博主允许不得转载。