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

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

设计模式简介

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

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

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

相比于这些好处最最重要的是,它的难度并不大,我们学习的成本不高。所以这是一件一本万利的事情,说是程序员进阶的必备技能也不为过。前面也说了设计模式是代码经验的总结和提炼,所以它也和语言特性有关,不同的语言实现出来的设计模式以及能够实现的设计模式也不一样。主流一般流行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. 【FJOI2007】轮状病毒 - Matrix-Tree定理

    题目描述 轮状病毒有很多变种.许多轮状病毒都是由一个轮状基产生.一个n轮状基由圆环上n个不同的基原子和圆心的一个核原子构成.2个原子之间的边表示这2个原子之间的信息通道,如下图所示. n轮状病毒的产生 ...

  2. iptables 表与链的对应关系

    1)filter表——三个链:INPUT.FORWARD.OUTPUT作用:过滤数据包 内核模块:iptables_filter. 2)Nat表——三个链:PREROUTING.POSTROUTING ...

  3. python2-dpkt 下载

    原文链接:https://centos.pkgs.org/7/forensics-x86_64/python2-dpkt-1.9.2-2.el7.noarch.rpm.html .Download c ...

  4. jQuery 筛选方法

    前言 在jQuery中所有的东西全部都包含在jQuery对象中,并没有单独的DOM元素这一说法. 要想获取单独的DOM元素请用[index]获取,下面介绍的所有方法都会返回新的jQuery对象,而不是 ...

  5. 哇,ElasticSearch多字段权重排序居然可以这么玩

    背景 读者提问:ES 的权重排序有没有示列,参考参考? 刚好之前也稍微接触过,于是写了这篇文章,可以简单参考下. 在很多复杂的业务场景下,排序的规则会比较复杂,单一的降序,升序无法满足日常需求.不过 ...

  6. 小案例-WebSocket实现简易聊天室

    前言 在详解 HTTP系列之一讲到HTTP/2.0 突破了传统的"请求-问答模式"这一局限,实现了服务器主动向客户端传送数据.而本章将通过一种在单个TCP连接上进行全双工通信的协议 ...

  7. 8点了解Java服务端单元测试

    一. 前言 单元测试并不只是为了验证你当前所写的代码是否存在问题,更为重要的是它可以很大程度的保障日后因业务变更.修复Bug或重构等引起的代码变更而导致(或新增)的风险. 同时将单元测试提前到编写正式 ...

  8. SpringCloud Alibaba Nacos 服务注册

    业务服务接入Nacos服务治理中心 启动Nacos访问地址为:http://101.200.201.195:8848/nacos/ 创建bom工程用于管理依赖(下方附加源码地址) 准备工作完成后开始接 ...

  9. Flutter —布局系统概述

    老孟导读:此篇文章非常详细的讲解了 Flutter 布局系统的工作原理. 翻译自:https://itnext.io/flutter-layout-system-overview-c70bbe9ba9 ...

  10. zabbix-4.0-监控服务器的ping告警设置

    问题:一直在困惑如果一台服务器的网络发生故障或者断开时,怎么第一时间发现并去排查. 思路:利用zabbix平台监控服务器,监控ping这一项,设置一个报警,并使用脚本去提醒与通知,可使用邮件报警/短信 ...