规约模式,颤抖吧产品经理!再也不怕你乱改需求了

规约模式,颤抖吧产品经理!再也不怕你乱改需求了

大家好,今天来和大家聊聊规约模式。

规约模式的英文是Specification Pattern,这里的规约其实是一个表意的翻译,Specification直译过来是要求、技术说明、明确的意思。光看名字估计大家都是一脸懵逼,根本不知道这个设计模式大概会是一个什么样子。这也是设计模式的一个通病,就是内涵比较晦涩,很难通过名称来概括。

我们先来简单说说这里规约的意思,它的含义其实很简单,就是把代码当中的代码逻辑以及业务规则区分开。这样的话我们可以在不影响全局的情况下*地修改和组合这两个部分。

我知道大家看完这个解释估计还是似懂非懂,没有关系,我们来简单看一个例子即可。

举个例子

我们都知道QQ里有各种钻石会员,什么绿钻、粉钻之类的。每种会员呢对应一些特殊的权益,假设现在我们新开发了某一个功能,要提供给同时拥有绿钻和粉钻的用户使用。

我们来实现这个逻辑非常简单,只需要一个判断条件就可以了。


def is_satisfied(user):
    return user.isGreenAuth() and user.isPinkAuth()

但是这里有一个问题,这里的逻辑是写死的,时间久了之后,如果这个代码转交给其他人维护了,那么接手的人估计会一脸懵。根本不知道这里为什么要这么判断,也不知道这个函数代表的功能是什么,可能需要去翻很多代码或者是找很多文档才能解决疑惑。

这里的根本问题就是业务规则和实现逻辑混合在一起了,什么意思呢?这里的绿钻 + 粉钻的判断就是业务规则,是为了实现某项功能而制定的,产品经理要的是这个两者叠加的规则。而我们通过user.isGreenAuth() and user.isPinkAuth()来实现,相当于新建了一个规则,从功能上这当然没有问题。但是问题是这里的规则和代码是定死的。

假设说某一天产品经理提了一个新的需求,不仅需要绿钻 + 粉钻还需要年龄大于18岁,并且年收入超过10w才可以享受这个功能。那么带来的结果就是我们需要在这个is_satisfied函数当中加上许多代码,随着时间的推移这个函数当中的代码会变得越来越臃肿,并且很有可能以后这当中的一些判断边界需要修改,比如18岁改成20岁,比如10w改成30w等等,这些都是有可能的。当要修改的时候你会发现由于代码的耦合和混乱,改起来非常麻烦。

为了解决这个问题就需要引入规约。

规约的含义

规约的意思是把逻辑和规则区分开,规则的归规则,逻辑的归逻辑。

我们还用上面的例子来看,比如在新的需求当中,逻辑本身是很简单的。即粉钻 + 绿钻 + 年龄达标 + 收入达标,这个是规则,而年龄达标和收入达标则是具体的实现。

当我们把规则定好了之后,我们再去解构其中的组件,比如收入达标的定义是年收入大于10w,比如年龄达标的含义是大于18岁。我们可以把这些组件做成单独的模块,这样以后需要修改这些边界的时候,我们只需要在具体实现当中修改就可以了,对于规则的部分就不用动了。同样如果我们需要修改规则,我们也可以避免对实现的改动,这就是规约的意义。

首先,我们需要一个框架用来实现逻辑的组合。

在逻辑当中变量之间的关系只有三种,就是与或非。所以整个逻辑关系还是很清楚的。这里我们采取抽象的方法,先定义接口,再去做具体的实现。这是我们规约出来的条件,它当中有四个接口,分别是与或非的操作接口,以及一个is_satisfied的判断接口。


from abc import abstractmethod

class Specification:

    def and_specification(self, candidate):
        raise NotImplementedError()

    def or_specification(self, candidate):
        raise NotImplementedError()

    def not_specification(self, candidate):
        raise NotImplementedError()

    @abstractmethod
    def is_satisfied(self, candidate):
        pass

在Specification的基础上,我们进一步实现与或非执行的逻辑。这里的逻辑很好理解,就不多解释了。


class AndSpecification(Specification):
    _one = Specification()
    _other = Specification()

    def __init__(self, one, other):
        self._one = one
        self._other = other

    def is_satisfied(self, candidate):
        return bool(self._one.is_satisfied(candidate) and self._other.is_satisfied(candidate))

class OrSpecification(Specification):
    _one = Specification()
    _other = Specification()

    def __init__(self, one, other):
        self._one = one
        self._other = other

    def is_satisfied(self, candidate):
        return bool(self._one.is_satisfied(candidate) or self._other.is_satisfied(candidate))

class NotSpecification(Specification):
    _wrapped = Specification()

    def __init__(self, wrapped):
        self._wrapped = wrapped

    def is_satisfied(self, candidate):
        return bool(not self._wrapped.is_satisfied(candidate))

# 组合规则组件,也就是两个规约条件的逻辑组合
class CompositeSpecification(Specification):
    @abstractmethod
    def is_satisfied(self, candidate):
        pass

    def and_specification(self, candidate):
        return AndSpecification(self, candidate)

    def or_specification(self, candidate):
        return OrSpecification(self, candidate)

    def not_specification(self):
        return NotSpecification(self)

上面所有的类都是为了定义规则的,这些有了之后,我们只需要在它的基础上填充具体的业务判断逻辑就可以了。相当于我们把一个复杂的业务逻辑拆分了,拆分成了若干个子逻辑的组合。

class User:
    def __init__(self, age=18, incoming=0, green_auth=False, pink_auth=False):
        self.age = age
        self.incoming = incoming
        self.green_auth = green_auth
        self.pink_auth = pink_auth

class UserSpecification(CompositeSpecification):
    def is_satisfied(self, candidate):
        return isinstance(candidate, User)

class GreenUserSpecification(CompositeSpecification):
    def is_satisfied(self, candidate):
        return getattr(candidate, 'green_auth', False)

class PinkUserSpecification(CompositeSpecification):
    def is_satisfied(self, candidate):
        return getattr(candidate, 'pink_auth', False)

class UserAgeSpecification(CompositeSpecification):
    def is_satisfied(self, candidate):
        return getattr(candidate, 'age', 0) > 18

class UserIncomingSpecification(CompositeSpecification):
    def is_satisfied(self, candidate):
        return getattr(candidate, 'incoming', 0) > 10

这样我们就把具体的判断逻辑和规则剥离出来了,这样两者之间就互不影响了,最后我们来看下具体应用的例子。

ivan = User(green_auth=True)
lily = User(age=23, incoming=20, green_auth=True, pink_auth=True)

specification = UserSpecification().and_specification(PinkUserSpecification).and_specification(GreenUserSpecification).and_specification(UserAgeSpecification).and_specification(UserIncomingSpecification)

print(specification.is_satified(ivan))
print(specification.is_satified(lily))

这里的specification就是产品经理提的具体规则,如果以后需要修改我们只需要修改它的定义即可。如果某一条具体的判断逻辑需要变动,我们找到对应的代码改动,就不会影响逻辑以及其他业务代码了。

之前有大牛曾经说过,某种意义上来说设计模式的诞生,其实是为了应对产品经理们朝三暮四的需求变动而产生的。不知道大家看完这个例子有没有这样的感触,至少我是觉得挺有道理的。

好了,今天的文章就到这里,衷心祝愿大家每天都有所收获。如果还喜欢今天的内容的话,请来一个三连支持吧~(点赞、在看、转发)

上一篇:MP4:文件类型mp42转码方案


下一篇:Springboot使用Specification连表查询LEFT