大家好,这是一个全新的专题——设计模式

其实可以选择的专题还有好几个,为什么选择设计模式呢?原因也很简单,首先是设计模式简单、易学。干货的文章固然好,但是普适性往往不强。另外一个很重要的点就是设计模式学习的好处非常明显,如果学得好的话,会觉得自己的编码能力有了质的突破。这并不是夸大其词,很多人包括我,在学习的时候都曾经有过这种感觉。

设计模式简介

设计模式这个词我想大家应该都听说过,但是它究竟是什么意思可能很多人并不清楚。其实设计模式就是一种经验,就是一种前人总结出来反复印证过可以解决各种问题或者是做出各种优化的代码设计经验

以上是教科书当中的内容,下面是我个人的理解,在我看来设计模式主要有两种用途,第一种是优化我们的代码结构,让我们的代码更加健壮,设计更加合理。我们读大牛的代码常常惊叹,同样的功能他怎么这么简单就实现了,这个设计太巧妙了。设计模式就是这些令人惊叹的精彩设计的总结。第二种用途相对功利一些,是为了抵抗业务逻辑变动。这一点如果你还没有毕业的话,可能理解不深刻。在职场当中程序员最讨厌什么?其中很重要的一个点就是业务逻辑的变动,昨天才说了这里要这么设计,突然过了两天就改了。或者是过了几天突然增加了一个之前没有想到的需求。而我们使用设计模式,一定程度上可以抵御这样的变更,尽量减少需求变动带来代码的更改。

简单总结一下,学习设计模式一方面可以让我们的代码能力更强写出来更优雅更牛的代码,另一方面可以帮助我们应对职场中需求,提升我们的表现和产出。

相比于这些好处最最重要的是,它的难度并不大,我们学习的成本不高。所以这是一件一本万利的事情,说是程序员进阶的必备技能也不为过。前面也说了设计模式是代码经验的总结和提炼,所以它也和语言特性有关,不同的语言实现出来的设计模式以及能够实现的设计模式也不一样。主流一般流行Java来实现设计模式,不过由于我们之前没有介绍过Java相关的语法,我们这里选择使用Python的设计模式。代码参考借鉴了github中设计模式热门repo:patterns,链接:https://github.com/faif/python-patterns

好了,废话不多说,我们开始今天的内容。

目录模式

今天要介绍的设计模式叫做Catalog,翻译过来是目录的意思。我没有找到很好的中文资料,可能也许是因为Java当中不支持这种模式,而中文主流的设计模式都是Java为基础的。

目录设计模式的核心逻辑在于我们在一个类当中以方法的形式提供许多种功能,我们将这些功能以目录的形式存储在一个dict当中。我们在创建实例的时候通过不同的参数获取不同的功能,使用方在使用的时候不感知具体的参数。

这种设计模式有几种实现方式,我们一个一个来看。

代码示例

基础版本

class Catalog:
    def __init__(self, param):
        self._static_method_class = {'param_value_1': self._static_method_1, 'param_value_2': self._static_method_2}

        if param in self._static_method_class:
            self.param = param
        else:
            raise ValueError('Invalid value for param: {0}'.format(param))

    @staticmethod
    def _static_method_1(self):
        print('excuted method 1')

    @staticmethod
    def _static_method_2(self):
        print('excuted method 2')

    def main_method(self):
        self._static_method_class[self.param](self)

整个的逻辑很简单,我们在init Catalog这个类的时候创建了一个_static_method_class dict,在这个dict当中我们的key是一个字符串,value是Catalog这个类的两个静态方法。然后我们判断param在不在dict当中,如果不在的话说明传入的param有误,我们抛出一个异常。

在使用的时候调用的是main_method函数,在这个函数当中我们直接会根据self.param的当中的值执行对应的方法。这里我们使用了静态方法,静态方法的好处是当我们创建它的子类的时候,静态方法不会被子类覆盖。当然这个静态方法不是非常有必要,也可以去除静态的逻辑,就使用普通方法也是一样可以运行的。

实例版本

上面的实现没有问题,但是有一个地方有一点怪怪的,就是_static_method_class这个dict我们放在了实例当中。带来的问题是如果这个dict很大,并且我们创建的实例很多的话,会导致冗余。因为既然所有实例的这个dict内容都是一样的,那么干嘛存那么多份呢,我们只需要存一份就可以了呀。

怎么样才能做到只存一份呢?也很简单,我们只需要把这个dict从实例域转移到类域就可以了。也就是说把这个dict变成类当中的field,说白了也就是把它的定义挪到init方法外面。

class CatalogInstance:

    def __init__(self, param):
        self.x1 = 'x1'
        self.x2 = 'x2'
        if param in self._instance_method_choices:
            self.param = param
        else:
            raise ValueError('Invalid value for param: {0}'.format(param))

    def _instance_method_1(self):
        print('Value {}'.format(self.x1))

    def _instance_method_2(self):
        print('Value {}'.format(self.x2))

    _instance_method_choices = {'param_value_1': _instance_method_1, 'param_value_2': _instance_method_2}

    def main_method(self):
        self._instance_method_choices[self.param](self)

这里我们新增了一些细节,就是x1和x2这两个参数。在这个例子当中,这两个参数是写死的,但是实际上我们完全可以将它的初始化也写在init方法当中。整体的逻辑和上面的版本大同小异,应该都可以看懂。

唯一一点需要注意的是,当我们在类的内部调用实例的方法的时候,都是通过self.xxxx来调用的,我们在调用的时候,解释器会自动把当前实例作为第一个参数传入其中,这也是为什么实例级的方法前面第一个参数一定是self的原因。而这里,我们是把实例方法存在了dict里,通过dict取出来调用的。这种情况下,解释器并不会传入self,所以我们自己需要加上self这个参数,否则的话就会引发报错。

class和static版本

除了把_static_method_class这个dict放到了init方法的外面变成了类中的字段之外,我们还可以对这个dict存储的value做改动。我们可以把这两个方法变成类级别的方法和静态方法。虽然我个人觉得这样改动的意义不是很大,但是也是一种方法,大家可以参考一下。

把方法变成类级别方法:

class CatalogClass:

    x1 = 'x1'
    x2 = 'x2'

    def __init__(self, param):
        if param in self._static_method_choices:
            self.param = param
        else:
            raise ValueError('Invalid Value for param: {0}'.format(param))

    @classmethod
    def _static_method_1(cls):
        print('executed method 1')

    @classmethod
    def _static_method_2(cls):
        print('executed method 2')

    _static_method_choices = {'param_value_1': _static_method_1, 'param_value_2': _static_method_2}

    def main_method(self):
        self._static_method_choices[self.param].__get__(None, self.__class__)()

这里有一点需要注意,就是我们把x1和x2这两个参数也变成了类中的变量。因为在classmethod当中我们是无法调用到实例域下的变量的,所以必须要将它们变成类级别当中的变量才可以访问到。另外classmethod是规定了需要传入class的相关参数的,并且是不可以直接调用的,所以我们要使用__get__方法获取原始的函数。

把方法变成静态方法:

class CatalogStatic:

    def __init__(self, param):
        if param in self._static_method_choices:
            self.param = param
        else:
            raise ValueError('Invalid Value for param: {0}'.format(param))

    @staticmethod
    def _static_method_1():
        print('executed method 1')

    @staticmethod
    def _static_method_2():
        print('executed method 2')

    _static_method_choices = {'param_value_1': _static_method_1, 'param_value_2': _static_method_2}

    def main_method(self):
        self._static_method_choices[self.param].__get__(None, self.__class__)()

用法和上面类似,只是将classmethod换成了staticmethod而已。

用法浅谈

关于目录这个设计模式的讲解到这里就结束了,下面我们讨论一下它的使用方法,究竟在什么场景下我们会用到这么个设计模式呢?

其实很简单,就是我们实例创建和使用是分离的场景。比如我们是某功能的提供方,而使用方是另外的人。我们提供类的实例给对方使用,这样做的好处是如果一旦需求发生变化,比如说之前开发的功能A要加一些改动,我们只需要自己改动Catalog类当中的逻辑就可以了,下游可以不需要做任何修改。再比如我们可以把创建实例的时候传入的参数做成可配置的,这样我们就可以通过修改配置来调用不同的逻辑。

关于这个设计模式还有一些改动的方案,比如我们可以把参数的传递放在调用方。调用方获得的实例都是一样的,调用的时候传入不同的参数获得不同的效果。

今天的文章到这里就结束了,如果喜欢本文的话,请来一波素质三连,给我一点支持吧(关注、转发、点赞)。

原文链接,求个关注

- END -

{{uploading-image-108919.png(uploading...)}}

设计模式 | Catalog设计模式,抵御业务方需求变动的更多相关文章

  1. To B产品,业务方全程蒙蔽怎么搞?

            这是发生在很久前的事,那会我还是产品实习生.         今天和业务部门进行需求审核,对的是公司内部SAAS系统的采购模块.怎么说呢?就是觉得不专业吧         辛辛苦苦把原 ...

  2. 图解Java设计模式之设计模式七大原则

    图解Java设计模式之设计模式七大原则 2.1 设计模式的目的 2.2 设计模式七大原则 2.3 单一职责原则 2.3.1 基本介绍 2.3.2 应用实例 2.4 接口隔离原则(Interface S ...

  3. 23种设计模式+J2EE设计模式学习笔记-初识设计模式

    设计模式简介: 设计模式是一套被反复使用的.多数人知晓的.经过分类编目的.代码设计经验的总结.(个人理解:设计模式是不关乎业务,逻辑实现,针对普遍问题的一种解决方案). 设计模式的类型: 传统23种设 ...

  4. 设计模式课程 设计模式精讲 7-2 建造者模式Coding

    1 代码演练 1.1 建造者模式演练 1.2 静态内部类演练建造者模式(链式调用) 1 代码演练 1.1 建造者模式演练 需求: 根据讲师提供的课程名称,课程ppt,课程视频,课程手记,课程问答 制作 ...

  5. Java设计模式--单列设计模式

    设计模式:解决某一类问题行知最有效的方法.java有23种设计模式 单列设计模式: 解决一个类在内存中只存在一个对象 思路:(要保证对象的唯一性) 1.为了避免其它程序建立该对象,先禁止替他类创建改对 ...

  6. 图解Java设计模式之设计模式面试题

    图解Java设计模式之设计模式面试题 1.1 Java设计模式内容介绍 1.1.1 先看几个经典的面试题 1.1.2 设计模式的重要性 1.1 Java设计模式内容介绍 1.1.1 先看几个经典的面试 ...

  7. 流量治理神器-Sentinel 究竟是怎么做到让业务方接入简单?

    大家好,我是架构摆渡人,这是流量治理系列的第10篇原创文章,如果有收获,还请分享给更多的朋友. 做业务开发,需要考虑业务的扩展性.做基础框架开发,需要考虑如何让业务方接入,使用简单,尽量不要耦合在业务 ...

  8. 技术管理进阶——技术Leader如何拒绝业务方?

    原创不易,求分享.求一键三连 前段时间,有个粉丝在群里问了一个问题: 今天对接一个业务团队,要我帮他导数据,这种工作又臭又烦又没成长,而且边界模糊谁做都可以,我很想拒绝他,但又怕引起对方不满,大家有什 ...

  9. Asp.net设计模式笔记之三:业务逻辑层的组织

    本章内容要点: 1.Transaction Script模式组织业务逻辑 2.Active Record模式和Castle Windsor来组织业务逻辑 3.Domain Model模式来组织业务逻辑 ...

随机推荐

  1. 轻量级Java EE企业应用实战:Struts2+Spring5+Hibernate5/JPA2

    轻量级Java EE企业应用实战(第5版)——Struts 2+Spring 5+Hibernate 5/JPA 2整合开发是<轻量级Java EE企业应用实战>的第5版,这一版保持了前几 ...

  2. idea Maven项目 包下载不下来或者已经下载了就是飘红

    0.先在settings.xml加上阿里的镜像在刷新试试 <mirror> <id>aliyunmaven</id> <mirrorOf>*</m ...

  3. 精确到小数点后n位的两种方法

    引言:大家在写程序中多多少少会遇到这个问题,特别对一些初学者会出现这个问题,做个ACM竞赛的同学肯定都会用C语言的printf格式控制输出,但是习惯于用C++的同学也不是一点办法都没有啊,这篇blog ...

  4. SpringSecurity权限管理系统实战—八、AOP 记录用户、异常日志

    目录 SpringSecurity权限管理系统实战-一.项目简介和开发环境准备 SpringSecurity权限管理系统实战-二.日志.接口文档等实现 SpringSecurity权限管理系统实战-三 ...

  5. JDBC驱动程序分类

    JDBC驱动程序分类 JDBC驱动程序:各个数据库厂商根据JDBC的规范制作的 JDBC 实现类的类库 JDBC驱动程序总共有四种类型: 第一类:JDBC-ODBC桥. 第二类:部分本地API部分Ja ...

  6. 使用Spring Cloud Config统一管理配置,别再到处放配置文件了

    1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! 可配置是一个成熟软件系统应该提供的特性,而配置管理对于大型系统就显得十分重要,特别是对于拥有多个应用的微服务系统.可喜的是, ...

  7. 谱聚类的python实现

    什么是谱聚类? 就是找到一个合适的切割点将图进行切割,核心思想就是: 使得切割的边的权重和最小,对于无向图而言就是切割的边数最少,如上所示.但是,切割的时候可能会存在局部最优,有以下两种方法: (1) ...

  8. All in One 你想知道的 hacker 技术都在这里

    作者:HelloGitHub-小鱼干 hacker 这个词,大多数理解为黑客,而维基百科对其的定义为--黑客(Hacker)是指对设计.編程和计算机科学方面具高度理解的人,在本文中 hacker 主要 ...

  9. SpringBoot启动注解源码流程学习总结

  10. FlashFXP免费版下载

    http://www.wocaoseo.com/thread-294-1-1.html 关于互联网的上传下载软件可能有很多种,也有很多安装后免费使用的软件比如8UFTP等,但如果论起速度和功能性,个人 ...