为了程序更好的维护和扩展,在面向对象思维的世界里,首先是面向接口编程,然后我们应该把做什么和怎么做进行分离。
以下我将用一个开晚会的样例来演示一下,终于达到的效果是:工厂+反射+配置文件实现程序的灵活应用。会详细说明一下这个过程是怎么来的,明确了这个,就会对反射和配置文件的结合更加深刻一些。
想要实现的功能是:晚会有一个唱歌、舞蹈、小品的节目单,详细各个节目的表演者仅仅须要一个就能够,每个表演接口都有两个实现类(表演者)。通过client调用不同的实现类来实现不同的节目单。表演者就是“做什么”,那么“怎么做”就是节目单了。首先来看看类图结构:
以下放代码,首先是接口,即各类表演:
<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("王菲演唱:我愿意");
}
}
client调用方式:
<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();
}
}
那么client代码就变成了这样:
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!尽管对反射的详细实现原理和过程还不太清楚,但首先会用才干有继续研究的动力。继续努力中~~