Python类层次结构的问题
我有一个类的层次结构:
class ParentClass:
def do_something(self):
pass # child classes have their own implementation of this
class ChildClass1(ParentClass):
def do_something(self):
<implementation here>
class ChildClass2(ParentClass):
def do_something(self, argument_x):
<implementation here>
class ChildClass3(ParentClass):
def do_something(self, argument_y):
<implementation here>
这里有两个问题:
- 方法 do_something() 在子类中的接口不同:在子类2和子类3中,它接受一个参数,但在子类1中没有参数
- do_something() 的参数名称不同,强调它们在子类2和子类3中有不同的含义。下面的使用示例会让这个更清楚
这是这些类的使用方式:
有一个工厂类用来返回实例:
class ChildFactory:
def get_child(self, argument):
if argument == '1':
return ChildClass1()
elif argument == '2':
return ChildClass2()
elif argument == '3':
return ChildClass3()
在代码的后面:
...
# pseudocode, not python
child_type = ? # can have values '1', '2' or '3' at this moment
var1 = 1
var2 = 'xxx'
# var1 and var2 have different types, just to emphasize the difference in their
# meaning when being passed as arguments to do_something()
# this was mentioned above (the second problem)
child = ChildFactory.get_child(child_type)
if child is an instance of ChildClass1, child.do_something() is called
if child is an instance of ChildClass2, child.do_something(var1) is called
if child is an instance of ChildClass3, child.do_something(var2) is called
# end of pseudocode
问题:
- 上面提到的两个问题是设计不好的标志吗?如果是的话,正确的设计层次结构的方法是什么?
- 如何在Python中统一编写伪代码片段?主要关注点是避免为每个特定情况使用庞大的 if/else 语句,因为这会使 ChildFactory.get_child() 的 if/else 语句翻倍
4 个回答
如果一些东西可以互相替换使用,它们就应该有相同的接口。对于一个方法来说,这意味着它们需要有相同数量的参数,这些参数的含义和名称也要一样,并且顺序要一致。如果它们的表现不完全相同,那就给它们起不同的名字,让它们看起来像是可以互换的。
像这样抽象的问题很难回答。如果我们知道你想解决什么问题,那就简单多了。我可以告诉你,看到下面这样的代码通常不是个好兆头:
if isinstance(obj, cls1):
...
elif isinstance(obj, cls2):
...
这通常意味着你应该定义一个新的方法,而不是使用if/elif语句。在Python中,如果你觉得这样方便,可以在类定义外面定义方法。还有,如果两个可以互换的类有同样名字但参数数量不同的方法,这也是个坏兆头,这说明这两个类并不是真正可以互换的。要么这些不同的方法应该接受相同的参数,要么它们应该有不同的名字。此外,定义那些从未被调用的方法也是没有必要的,比如ParentClass中的do_something方法——这种情况常见于从C++、Java或C#转到Python的程序员身上。
方法如果名字相同但参数不同,这通常是个不好的设计。
“方法 do_something() 在子类中的接口不同:子类2和3接受一个参数,但子类1没有参数。”
你没有说明原因。其实有两个很好的理由:
子类1有一个默认值。
子类2忽略了这个值。
几乎其他任何理由都说明 do_something
是 真的 不同,应该给它起个不同的名字。
如果子类1有一个默认值,那就直接在方法的参数中写上这个默认值。
class ChildClass1( ParentClass ):
def do_something( argument_x= None )
....
如果子类1忽略了这个值,那就直接忽略它。别为了忽略一个值而费劲。
class ChildClass1( ParentClass ):
def do_something( argument_x )
return True
一个多态的方法如果不使用所有参数值,并没有什么特别之处。
“do_something() 的参数有不同的名字,以强调它们在子类2和3中有不同的含义。”
这其实是个糟糕的设计。你不能有同一个方法但参数名字不同,因为它们做的事情不一样。
同样的方法名是错误的。如果它们是类似事情的不同实现,那么参数的含义基本上应该是相同的。
如果它们实际上做的是 不同 的事情,那就没有多态性,你不应该给这些方法起相同的名字。
当两个类中的方法做的事情根本不同——需要不同的参数和明显不同的名字来表明这一点——这些方法 绝对不能 有相同的名字。当名字不能准确描述方法的实际功能时,它就失去了意义。
注意
顺便说一下,你的代码在Python中是可以工作的,因为Python使用鸭子类型。只要方法名匹配,参数类型甚至不需要接近匹配。然而,这样的设计真的很糟糕,因为这些方法之间的本质差异太大了。