对设计模式和OOP实践的思考如何在动态和弱类型语言中发生变化?


11

这些方面已经有了一个相当有用的问题(“ 非OOP设计模式? ”),但是我对于刚开始使用动态和弱类型语言的人的过渡观点感到更加好奇。

那就是:假设我已经使用C ++,C#或Java进行编程多年,并从GoF设计模式,Fowler 企业应用程序体系结构模式SOLID原理等方面吸取了很多智慧。 m涉猎Ruby,Python,JavaScript等,并想知道我的知识如何应用。大概在很多情况下我都可以进行直接翻译,但是几乎可以肯定的是,这并不能充分利用我的新设置。仅仅靠鸭子打字就可以改变我很多基于界面的思想。

什么保持不变?有什么变化?是否有新手应该知道的诸如SOLID之类的指导原则或动态语言新手应该知道的规范模式(也许是全新的)?

Answers:


7

什么保持不变?有什么变化?

模式是相同的。语言技巧不断变化。

是否有SOLID等指导原则,

是。实际上,它们仍然是指导原则。没有什么变化。

还是动态模式新手应该知道的规范模式(也许是全新的)?

有些事情是独特的。通常影响是实施技术会发生变化。

模式是-很好- 模式。不是法律。不是子例程。不是宏。这只是一个好主意,因为它是一个好主意,所以会被重复。

好的想法不会过时或不会发生重大变化。

其他说明。Python不是“弱类型”。由于没有强制转换操作,因此它比Java或C ++具有更强的类型。[是的,有一种方法可以弄虚与某个对象关联的类,但是除了证明一个挑剔,合法的观点外,这不是一件可以做的事情。]

也。大多数设计模式基于利用多态性的不同方法。

StateCommandMemento 为例。它们具有用于创建多态状态,状态改变的命令或纪念品的类层次结构。在Python中进行此操作时,没有任何明显变化。较小的更改包括放松了精确的类层次结构,因为Python中的多态性依赖于通用方法,而不是通用祖先。

而且,某些模式只是试图实现后期绑定。大多数与工厂相关的模式都是尝试在不重新编译应用程序中每个C ++模块的情况下轻松更改类层次结构。在动态语言中,这并不是那么有趣的优化。但是,工厂作为一种隐藏实现细节的方法仍然具有巨大的价值。

一些模式是尝试驱动编译器和链接器的。 例如,存在Singleton以创建令人困惑的全局变量,但至少封装它们。Python单例类的前景并不理想。但是Python模块已经是单例了,因此我们许多人只使用一个模块,而避免尝试弄乱Singleton类。


我不会说SOLID不会“改变”。根据语言及其对象模型的不同,“开闭原则”和“李斯科夫替代原则”可能都没有意义。(我会想到JavaScript和Go。)
Mason Wheeler

@梅森·惠勒 根据我的经验,开闭是与语言无关的。您将不得不提供一些更具体的示例,说明使用JavaScript或Go进行开闭式设计如何“毫无意义”。也许Liskov替换不适用于JavaScript,但是基本模式-多态性-似乎仍然适用。
S.Lott

@ S.Lott:编辑中有不错的更新;他们比原始答案有趣得多:P。感谢您纠正我的Python错误。通常,特定的模式示例以及它们如何与动态语言,多态性,后期绑定等联系在一起都是很完美的。
Domenic

@ S.Lott:因为打开/关闭与继承有关,而这些语言却没有。(此外,对象“被关闭以进行修改”的想法在很多Ruby编码器中都无法
Mason Wheeler

@梅森·惠勒:感谢您对“打开/关闭”的说明。我认为JavaScript异常很重要,但是由于问题如此开放(列出JavaScript,Python和Ruby,以及名为ETC的语言),所以我不确定如何解决这种特殊情况。
S.Lott


8

使用动态的面向对象语言进行编程使用许多相同的模式和原理,但是由于环境的缘故,存在某些调整和差异:

用Duck Typing替换接口 -四人帮将告诉您使用具有纯虚函数的抽象基类,并且您将使用Java中的接口(以动态语言),您只需要了解即可。由于您可以在任何地方使用任何对象,并且如果实现了实际调用的方法,它将可以正常工作,因此您无需定义正式接口。可能值得记录一下,以便清楚实际需要什么。

功能也是对象 -有许多模式将决策与行动区分开;命令,策略,责任链等。在具有一流功能的语言中,通常简单地传递一个功能而不是使用.doIt()方法来创建对象通常是合理的。这些模式转换为“使用高阶函数”。

已售 -由于没有接口,因此接口隔离原则在这里受到最大的打击。您仍然应该考虑该原理,但是不能将其转化为代码。只有个人的警惕会在这里保护您。从正面看,在常见的动态环境中,违反此原理引起的痛苦将大大减少。

“……以我个人的观点……成语!” -每种语言都有好的做法和坏的做法,如果您想要这些语言的最佳代码,则必须学习它们并加以遵循。例如,用内置列表理解的语言可能会嘲笑完美编写的迭代器模式。


3

以我的经验,某些模式在Python中仍然有用,甚至比在更多静态语言中更容易设置。某些图案OTOH只是不需要,甚至不喜欢,例如Singleton图案。请改用模块级别的变量或函数。或使用博格模式。

无需设置创建模式,它通常足以传递可创建对象的可调用对象。那可能是一个函数,一个带有__call__方法的对象,甚至是一个类,因为new()在Python中没有,只有类本身的调用:

def make_da_thing(maker, other, stuff):
    da_thing = maker(other + 1, stuff + 2)
    # ... do sth
    return da_thing

def maker_func(x, y):
     return x * y

class MakerClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
...
a = make_da_thing(maker_func, 5, 8)
b = make_da_thing(MakerClass, 6, 7)

状态和策略模式在C ++和Java等语言中具有非常相似的结构。在Python中则更少。策略模式或多或少保持不变,但是状态模式几乎变得不必要。静态语言中的状态模式在运行时模拟类的更改。在Python中,您可以做到这一点:在运行时更改对象的类。只要以受控的封装方式进行操作,就可以了:

class On(object):
    is_on = True
    def switch(self):
        self.__class__ = Off

class Off(object):
    is_on = False
    def switch(self):
        self.__class__ = On
...

my_switch = On()
assert my_switch.is_on
my_switch.switch()
assert not my_switch.is_on

依赖于静态类型分派的模式将无法工作,或者工作方式会完全不同。您不必编写过多的样板代码,例如,Visitor Pattern:在Java和C ++中,您必须在每个可访问类中编写一个accept方法,而在Python中,您可以通过mixin类继承该功能,例如Visitable:

class Visitable(object):
    def accept(self, visitor):
        visit = getattr(visitor, 'visit' + self.__class__.__name__)
        return visit(self)
...

class On(Visitable):
    ''' exactly like above '''

class Off(Visitable):
    ''' exactly like above '''

class SwitchStatePrinter(object): # Visitor
    def visitOn(self, switch):
         print 'the switch is on'
    def visitOff(self, switch):
         print 'the switch is off'

class SwitchAllOff(object): # Visitor
    def visitOn(self, switch):
         switch.switch()
    def visitOff(self, switch):
         pass
...
print_state = SwitchStatePrinter()
turn_em_off = SwitchAllOff()
for each in my_switches:
    each.accept(print_state)
    each.accept(turn_em_off)

在Python中,许多要求以静态语言应用模式的情况并没有那么多。许多其他问题也可以解决,例如高阶函数(装饰器,函数工厂)或元类。


现在,我意识到您的答案几乎涵盖了我刚刚提出的问题:重写__class__以Python实现工厂是个好主意吗?
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.