从一个注册模式用例聊开闭价值
不知道你们平时是否使用注册模式设计模式,我一开始的感觉也是它把一简单的问题变得复杂,所以我不是非常喜欢,但我又非常追求代码的开闭,所以我又常常不得不用它。
我的需求是这样的,我有以下两个类,一个叫Str,一个叫Int,它并非是python的built-in类型,而是在其基础上做一定的处理。我处理的最终目的是能够让它变成SQL语句。
假设他们是这样的:
现在我有一个Raw的类型,我接受一个原生的python对象,它可能是str,可能是int,它还可能是float/bool/tuple/list/set甚至NoneType(更多的情况我没有在例子中展示),我需要根据Raw区别这个对象的type并创建属于我自己的IType。一开始我觉得这个问题很简单,因为重写__new__加上if语句就可以,比如这样:
这个异常可以在__new__中直接抛出,也可以允许它创建一个Raw对象,在调用它的to_string方法时抛出。我暂时没有觉得这两种行为有什么优劣(如果你们觉得有很大区别可以提出来)。我觉得这个问题圆满解决了。
问题是在于,当我需要处理的type变多的时候,我都工作就变得很复杂,我需要频繁修改这个if语句。大家都知道,修改if语句等于在开闭上画一个大大的叉,这是我不能容忍的。
所以我简单优化了一下,在左思右想后,我觉得我适当应用了注册模式:
当然这个代码是一个例子,它直接在__new__中抛出了异常,但这不重要。
这段代码也是可以优化的,比如给Raw创建一个create方法(这样做的意义有些模糊,具体是重写__new__让其返回不同的类型,还是规定统一调用create方法创建类型,这个问题其实我也没想出比较满意的答案,本质在于你是否需要一个create去取代__init__或者__new__这个过程,或许在其它语言里面是很有必要的,因为它们不一定有__new__这种魔术方法);在每个类(Str,Numeric)中创建create方法,甚至给它写一个Factory(这里涉及到了工厂模式,但是这个例子其实很简单,我并不认为这里给每一个类型增加一个工厂有什么意义),然后在工厂里面写这个create,并对所有的工厂做出要求实现这个create。简单的问题复杂化也是我不太喜欢的,所以我就一切从简了。
但我发现了一个很严肃的问题,我现在从修改if语句变成了修改一个dict,我的工作量减少了。但是:
修改一个if语句,和修改一个dict,有什么本质的区别呢?不还是对修改开放的吗?
所以我只能再折腾一下,搞一个注册类:
这样是真的实现了,当我需要重新处理一个类的时候,我只需要新增一行register。
它确实实现了开闭。
但它让代码变简单了吗?没有。所以一切的价值都在于开闭上。问题是开闭真的有价值吗(这个问题似乎答案肯定是:是的。但我换种问法,我们都知道开闭是为了增加代码可读性,降低维护成本,在我当前需求下,我去修改if语句,维护成本真的很高吗?我编写注册类的时间成本是否和我后续的维护成本匹配?我不知道,因为大家会说,我自己也会说:你自己到最后也无法知晓你的需求会变得多么的复杂)
所以这是个很玄学的问题,仁者见仁智者见智,欢迎大家指摘。

