关于轻设计模式doHandler模式的运用与实现

引言

在实际的开发中,很有可能我们写好了很多的模块,但是突然要增加一个入侵很多接口的需求。比如,本来正常的登陆,发布文章,分享文章,是很常见的基本功能。但是老板突然有一点来说,我们要留存用户,要增加一些积分规则和活动,登录的时候我们要给它加分;鼓励用户发文章,发文章我们要给他们加分等等。

那么如何针对这种同一个入口进入,且实现的内容不完全相同的模式进行处理呢?

这里就引发了利用spring上下文实现的轻设计模式

doHandler模式的分析与实现

下面的内容都会按照引言中的业务需求来进行逐步实现。

一、 整体考虑

首先有各种各样的加分规则,那么可以先采用一个枚举类去保存加分的编号和分值

@AllArgsConstructor
@Getter
public enum PointEnum {
    /**
     * 编号1:发布文章,增加2分
     */
    PUBLISH_ARTICLE(1,2),
    /**
     * 编号2:分享文章,增加3分
     */
    SHARE_ARTICLE(2,3),
    /**
     * 编号3:登录签到,增加1分
     */
    LOGIN_SIGN(3,1),
    ;
    /**
     * 编号
     */
    private Integer code;
    /**
     * 分值
     */
    private Integer point;
}

其次,还要有一个统一的service书写抽象的addPoint()接口。同时,为了后面能区别不同的分值实现,这个service应该还要具有一个getCode()方法。

public interface PointHandlerService {

    /**
     * 增加分值
     * @return
     */
    boolean addPoint();

    /**
     * 返回的是不同情况下枚举的值
     * @return
     */
    Integer getCode();
}
二、 spring InitializingBean和 ApplicationContextAware的运用

我们要在spring容器初始化的时候,要将PointHandlerService 的不同实现都要加入到容器中,同时我们要内部维护一个Map用来存储它的实现,以便我们能快捷的取到。

@Component
public class PointHandlerInterceptor implements InitializingBean, ApplicationContextAware {

    /**
     * 内部维护的Map
     */
    private Map<Integer, PointHandlerService> pointHandlers = new HashMap<>();

    private ApplicationContext applicationContext;

    /**
     * 公共的方法
     * @param type
     * @return
     */
    public Boolean pointAdd(Integer type){
        PointHandlerService pointHandlerService = pointHandlers.get(type);
        return pointHandlerService.addPoint();
    }

    /**
     * 初始化pointHandlers这个Map
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        Map<String, PointHandlerService> noticeHandlerBeans = applicationContext.getBeansOfType(PointHandlerService.class);
        Collection<PointHandlerService> values = noticeHandlerBeans.values();
        for (PointHandlerService noticeHandler : values) {
            Integer type = noticeHandler.getCode();
            pointHandlers.put(type, noticeHandler);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

这样子对于内部的函数采用pointHandlers.get(type)就可以获得相应的PointHandlerService 的实现类。

三、 实现类简单demo
@Component
public class PointLoginSignServiceImpl extends PointBaseSeriveImpl implements PointHandlerService {
    @Override
    public boolean addPoint() {
        //这里写上要处理的业务逻辑
        System.out.println("登录加分:"+PointEnum.LOGIN_SIGN.getPoint());
        return Boolean.TRUE;
    }

    @Override
    public Integer getCode() {
        return PointEnum.LOGIN_SIGN.getCode();
    }
}
@Component
public class PointPublishArticleServiceImpl  extends PointBaseSeriveImpl implements PointHandlerService {
    @Override
    public boolean addPoint() {
        //这里写上要处理的业务逻辑
        System.out.println("发布文章加分:"+PointEnum.PUBLISH_ARTICLE.getPoint());
        return Boolean.TRUE;
    }

    @Override
    public Integer getCode() {
        return PointEnum.PUBLISH_ARTICLE.getCode();
    }
}

撰写测试方法

@RunWith(SpringRunner.class)
@SpringBootTest
public class HandlerdemoApplicationTests {

    @Autowired
    private PointHandlerInterceptor pointHandlerInterceptor;

    @Test
    public void contextLoads() {
        pointHandlerInterceptor.pointAdd(PointEnum.LOGIN_SIGN.getCode());
        pointHandlerInterceptor.pointAdd(PointEnum.PUBLISH_ARTICLE.getCode());
        pointHandlerInterceptor.pointAdd(PointEnum.SHARE_ARTICLE.getCode());
    }
}

最后结果如下:

图1.结果图

拓展与优化

  1. 在pointHandlers.get(type)的时候可以直接返回service对象。这样子做的好处在于,拿到子类的service,以便可能会调用其他方法。

2.service层可能要传入一些BO对象,此时可以修改成如下写法:

public interface PointHandlerService<T> {

    /**
     * 增加分值
     * @return
     */
    boolean addPoint(T obj);

。。。省略后面代码
}

在实现的时候写明是哪个BO对象,有利于利用java强对象规则在编译阶段进行校验。另外serviceImpl也可以抽取一个公共的类进行继承的形式。

@Component
public class PointLoginSignServiceImpl extends PointBaseSeriveImpl implements PointHandlerService<LoginSignBO> {
    @Override
    public boolean addPoint(LoginSignBO loginSignBO) {
        //这里写上要处理的业务逻辑
        System.out.println("登录加分:"+PointEnum.LOGIN_SIGN.getPoint());
        return Boolean.TRUE;
    }

。。。省略后面的代码

BO对象也可以抽取出一些公共的参数与让各个不同的BO对象继承。

  1. 第二点说的是基于公共的抽取出来。那么有的时候可能会在一些实现类里面增加一些方法,其他的实现类不需要。比如说,在发布文章和分享文章完成后,要对文章的分享数进行一个统计,而登陆就不需要。
    当然可以采取在发布文章和分享文章的实现类里面实现,但是这样子类的一个方法业务逻辑就不够纯正。另外有可能这个需求是一时的,比如在推广的时候这个统计非常必须,到后面会干掉的。直接修改实现类内部未免入侵性过大。
    这个时候可以采取在service增加一个额外的接口,但是这个接口是default类型的!
    /**
     * 并不是必须的实现
     */
    default boolean notNeedImpl(){
        System.out.println("I am the PointHandlerService");
        return Boolean.TRUE;
    }

这是java8的特性,接口也可以写默认的实现方法。这样子在一些子类有所不同的时候,只要复写该方法,就会调用实现类的,而不是接口的了。测试代码如下:

    @Test
    public void contextLoads() {
        PointHandlerService p1 = pointHandlerInterceptor.pointAdd(PointEnum.LOGIN_SIGN.getCode());
        p1.addPoint(new LoginSignBO());
        p1.notNeedImpl();
        PointHandlerService p2 =pointHandlerInterceptor.pointAdd(PointEnum.PUBLISH_ARTICLE.getCode());
        p2.addPoint(new PublishArticleBO());
        p2.notNeedImpl();
        PointHandlerService p3 =pointHandlerInterceptor.pointAdd(PointEnum.SHARE_ARTICLE.getCode());
        p3.addPoint(new ShareArticleBO());
        p3.notNeedImpl();
    }

最终实现结果如下:

图2.测试代码结果

以上代码均放在了我的github博客上面,欢迎各位读者下载查阅。

https://github.com/YukunWen/HandlerDemo

感谢阅读,如有帮助,烦请点赞,谢谢!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,664评论 18 139
  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,748评论 0 14
  • 孩子的成长,与成人的关系密不可分。 孩子真的该守规矩嘛? 什么是规矩? 你守规矩嘛? 你有让孩子守规矩嘛?
    张鑫Jackie阅读 206评论 0 0
  • 我轻轻地踱着 踱着清浅的脚步 细碎的雨丝飘着 清清凉凉 温温柔柔 我闭上眼 感受风的温煦 雨的迷离 也是那年雨微落...
    伊青玥阅读 319评论 2 5
  • 1.活鱼会逆流而上,死鱼才会随波逐流 2.一件事被所有人都认为是机会的时候,其实它已不是机会了 3.不要抱着过去不...
    gyp郭云鹏阅读 478评论 0 1