我们在写业务的时候,比如说三国杀或者一些卡牌游戏,设定了雷杀,火杀,这些牌都有一个特征就是指定一个人掉血,掉多少血,各不相同,那么我们就设定雷杀一血,火杀二血,水杀三血。
在设计的时候,我们很蠢的设计就是这样。
转化为代码,就是这样
public static void main(String[] args) {
// 模拟输入
String cardName="ThunderKill";
switch (cardName){
case "ThunderKill":
new ThunderKill().kill();
break;
case "FireKill":
new FireKill().kill();
break;
case "WaterKill":
new WaterKill().kill();
break;
}
}
public class FireKill {
public void kill(){
System.out.println("火杀 -2血");
}
package card;
public class WaterKill {
public void kill(){
System.out.println("水杀 -3血");
}
}
package card;
public class ThunderKill {
public void kill(){
System.out.println("雷杀 -1血");
}
}
}
这样写有个坏处,那就是比如说我加了个土杀,然后新增个class,还得再新实例化一个对象出来,,对代码改动太大,那么我们改造一下,对其进行抽象化。
改造成这样,让用户不管执行的是哪个对象的杀,执行的就是杀,对象执行和初始化分开,这样的好处在于不用挨个去进行kill操作,只需判断实例化哪个对象就行。
public static void main(String[] args) throws Exception {
String cardName="ThunderKill";
Card card;
switch (cardName){
case "ThunderKill":
card=new ThunderKill();
break;
case "FireKill":
card=new FireKill();
break;
case "WaterKill":
card=new WaterKill();
break;
default:
throw new Exception();
}
card.kill();
}
}
public class FireKill {
public void kill(){
System.out.println("火杀 -2血");
}
package card;
public class WaterKill {
public void kill(){
System.out.println("水杀 -3血");
}
}
package card;
public class ThunderKill {
public void kill(){
System.out.println("雷杀 -1血");
}
}
public interface Card {
public void kill();
}
这个版本的好处在于我们不管实例化哪个对象,直接在判断中实例出来,然后统一进行执行操作
这样还不够,我们再看一个版本
package card.v2;
import card.v2.cards.FireKill;
import card.v2.cards.ThunderKill;
import card.v2.cards.WaterKill;
public class CardFactory {
static Card getCard(String cardName) throws Exception {
Card card;
switch (cardName) {
case "ThunderKill":
card = new ThunderKill();
break;
case "FireKill":
card = new FireKill();
break;
case "WaterKill":
card = new WaterKill();
break;
default:
throw new Exception();
}
return card;
}
}
public static void main(String[] args) throws Exception {
String cardName="ThunderKill";
Card card=CardFactory.getCard(cardName);
card.kill();
}
至此为止,我们已经获得一个相对稳定的结构了,肤浅一点来说,看这个代码稳定与否,是写了多少个new是吗?
第一个版本的代码我们有Card就实例化一个Card,第二个版本我们先把Card抽象出来,按照传入的参数进行实例化,最后执行某个方法,第三个新建个工厂,将执行的这个功能的转移到工厂类中,需要什么对象,直接从工厂中获取,其实第二个版本和第三个版本本质上并没有太大区别,只是功能上的转移。
那么,我们能不能再有个更优雅一点的写法?
有,那就是映射
其实很多场景下我们使用映射并不多,但是映射的确比较好用。
先科普一下什么是元类,
首先类是对象的一种抽象,元类就是对类的一种描述
元类写法Class<?>clazz
我们来看最后一个版本的代码
public Card getCard(String name) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName(name);
Object obj = clazz.getDeclaredConstructor().newInstance();
return (Card) obj;
}
我们首先用了反射中的元类进行查找这个类,再用映射给其实例化出了一个对象
那么值得注意的是,8以及8之前的jdk支持直接newInstance这个方法,但是8以后java就已经废弃了这个方法,所以现在需要声明一个构造函数再进行实例化操作。
这样我们就可以进行动态传值实例对象,是不是很有趣?