SpringBoot利用反射加工厂模式减少代码中的If Else案例

情景描述:

你的同学是一个练习生, 他擅长🎤、💃、👅、🏀,并且希望在学校或班级派对上表演一个才艺。

他想收集同学的意见,决定是在学校还是班级进行哪一项派对活动才艺表演😄。

通常做法:

1. 创建派对接口:
/**
 * 派对活动接口
 */
public interface PartyService {

   public String sing();       // 唱
   public String dance();      // 跳
   public String rap();        // rap
   public String basketball(); // 篮球

}
2. 班级派对活动:
/**
 * 班级派对
 */
@Service("classPartyService")
public class ClassPartyServiceImpl implements PartyService {
    @Override
    public String sing() {
        System.out.println("在班里唱");
        return "在班里唱";
    }
    @Override
    public String dance() {
        System.out.println("在班里跳");
        return "在班里跳";
    }
    @Override
    public String rap() {
        System.out.println("在班里rap");
        return "在班里rap";
    }
    @Override
    public String basketball() {
        System.out.println("在班里打篮球");
        return "在班里打篮球";
    }
}
3. 学校派对活动:
/**
 * 学校派对
 */
@Service("schoolPartyService")
public class SchoolPartyServiceImpl implements PartyService {
    @Override
    public String sing() {
        System.out.println("在学校唱");
        return "在学校唱";
    }
    @Override
    public String dance() {
        System.out.println("在学校跳");
        return "在学校跳";
    }
    @Override
    public String rap() {
        System.out.println("在学校rap");
        return "在学校rap";
    }
    @Override
    public String basketball() {
        System.out.println("在学校打篮球");
        return "在学校打篮球";
    }
}
4. 派对Controller层:
/**
 * 派对Controller
 */
@RestController
@RequestMapping("/party")
public class PartyController {
    @Resource
    private PartyService classPartyService;

    @Resource
    private PartyService schoolPartyService;

    /**
     * 表示才艺地点: 班级Service
     * @param methodName  表示才艺方法: 唱、跳、rap、篮球
     */
    @GetMapping("/classPartyService/{methodName}")
    public String party(@PathVariable(value = "methodName") String methodName) {
        if(methodName == "sing"){
           return classPartyService.sing();
        }else if(methodName == "dance"){
           return classPartyService.dance();
        }else if(methodName == "rap"){
           return classPartyService.rap();
        }else if(methodName == "basketball"){
           return classPartyService.basketball();
        }
        return "没有该派对活动";
    }
    
    /**
     * 表示才艺地点: 学校Service
     * @param methodName  表示才艺方法: 唱、跳、rap、篮球
     */
    @GetMapping("/schoolPartyService/{methodName}")
    public String party(@PathVariable(value = "methodName") String methodName) {
        if(methodName == "sing"){
           return schoolPartyService.sing();
        }else if(methodName == "dance"){
           return schoolPartyService.dance();
        }else if(methodName == "rap"){
           return schoolPartyService.rap();
        }else if(methodName == "basketball"){
           return schoolPartyService.basketball();
        }
        return "没有该派对活动";
    }
}

优化后做法:

1. 添加工厂类:

工厂类只负责初始化和生产Service对象和方法(当然这里的Service对象是从Spring进货来的),方便提供给调用者,满足单一职责原则

/**
 * 派对工厂
 */
@Component
public class PartyFactory implements CommandLineRunner {
    @Resource
    private ApplicationContext applicationContext;

    private static Map<String, PartyService> partyServiceMap = new HashMap<>(); // 存放所有partyService
    private static Map<String, Method> partyMethodMap = new HashMap<>();        // 存放所有partyService的方法

    public PartyService getService(String serviceName){ // 提供Service对象
        return partyServiceMap.get(serviceName);
    }

    public Method getServiceMethod(String serviceName, String methodName){ // 提供Service方法
        return partyMethodMap.get(serviceName + ":" + methodName);
    }

    /**
     * 初始方法,启动执行
     */
    @Override
    public void run(String... args) throws Exception {
        partyServiceMap = applicationContext.getBeansOfType(PartyService.class); // 获取所有实现PartyService接口的实现类
        partyServiceMap.forEach((key, value) -> {
            Arrays.stream(value.getClass().getDeclaredMethods()).forEach(method -> { // 反射获取所有的方法
                partyMethodMap.put(key + ":" + method.getName(), method); // 将所有的方法push到map中
            });
        });
        partyMethodMap.keySet().forEach(System.out::println); // 打印所有的方法
    }
}
2. 修改Controller层:

这里的控制层也不需要依赖具体的某个Service对象,只从工厂中获取方法执行就行了,做到了解耦

/**
 * 派对Controller
 */
@RestController
@RequestMapping("/party")
public class PartyController {
    @Resource
    private PartyFactory partyFactory;

    /**
     * @param serviceName 表示才艺地点: 学校Service或班级Service
     * @param methodName  表示才艺方法: 唱、跳、rap、篮球
     */
    @GetMapping("/{serviceName}/{methodName}")
    public String party(
            @PathVariable( value = "serviceName") String serviceName,
            @PathVariable( value = "methodName") String methodName) {
        try {
            // 获取工厂中的对象和方法,并执行对象的方法
           return partyFactory.getServiceMethod(serviceName, methodName).invoke(partyFactory.getService(serviceName), null).toString();
        } catch (Exception e) {
            return "未找到方法,派对活动不存在!";
        }
    }
}
3.启动应用:

访问接口:http://localhost:8080/party/schoolPartyService/dance等链接

总结:

  1. 如果我们需要增加一个家庭派对活动比如HomePartyServiceImpl,那么只要实现PartyService就行了,不需要修改工厂和控制层的其他代码,满足了扩展性,符合开闭原则的设计。

  2. 上述只是一个简单的例子,可以理解为把所有的方法都存入一个map集合中,然后根据key去调用方法,有点像表驱动设计。

  3. 并不是所有的业务都需要抽象成设计模式,一般是业务比较稳定之后可以用设计模式优化代码或者是业务的变化比较有规律和通用。

  1. 如果你的业务是不按套路出牌,甲方和需求也反复横跳,那在现在敏捷开发的模式下,还真不如If Else来的快😂。要不然你精心抽象的代码,上的设计模式,突然需求业务变更,那就草了。
  1. 在设计中,代码的可读性和可维护性也是非常重要的。
案例仓库: Github链接
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容