哪些 OOP 原则(如果有)不适用或在动态类型环境中与静态类型环境中应用不同(例如 Ruby 与 C#)?这并不是呼吁进行静态与动态辩论,而是我想看看该分歧的任何一方是否都有公认的原则适用于一方而不适用于另一方,或者以不同的方式适用。像“更喜欢组合而不是继承”这样的短语在静态类型 OOP 文献中是众所周知的。它们同样适用于动态方面吗?

例如,在动态类型环境中,耦合的粒度似乎不会超出方法的级别。换句话说,任何给定的函数调用仅将调用者耦合到该特定接口,这 任何 class 可能会满足——或者换句话说,任何像那只鸭子一样嘎嘎叫的东西。

另一方面,在 Java 中,耦合的粒度可以达到包的高度。特定的方法调用不仅与另一个类/接口建立契约,而且还将其耦合到该类/接口的包/jar/程序集中。

这样的差异是否会导致不同的原则和模式?如果是的话,这些差异是否已得到阐明?里面有一个部分 红宝石镐 这本书有点朝这个方向发展(鸭子打字/类不是类型),但我想知道是否还有其他内容。我知道 Ruby 中的设计模式 但还没读过。

编辑——有人认为 利斯科夫 在动态环境中的应用与在静态环境中的应用不同,但我不禁认为它确实如此。一方面,没有与整个班级签订高级合同。但并非所有对任何给定类的调用都构成 隐含的 需要由子类按照 Liskov 规定的方式满足的合同?考虑以下。“做一些酒吧的事情”中的调用创建了一个需要由子班参加的合同。这不是“将专用对象视为基类吗?”的情况:

class Bartender
    def initialize(bar)
       @bar = bar
    end

    def do_some_bar_stuff
        @bar.open
        @bar.tend
        @bar.close
    end
end

class Bar
    def open
        # open the doors, turn on the lights
    end
    def tend
        # tend the bar
    end
    def close
        #clean the bathrooms
    end
end

class BoringSportsBar < Bar
    def open
        # turn on Golden Tee, fire up the plasma screen
    end

    def tend
        # serve lots of Bud Light
    end
end

class NotQuiteAsBoringSportsBar < BoringSportsBar
    def open
        # turn on vintage arcade games
    end
end

class SnootyBeerSnobBar < Bar
    def open
        # replace empty kegs of expensive Belgians
    end

    def tend
        # serve lots of obscure ales, porters and IPAs from 124 different taps
    end
end

# monday night
bartender = Bartender.new(BoringSportsBar.new)
bartender.do_some_bar_stuff

# wednesday night
bartender = Bartender.new(SnootyBeerSnobBar.new)
bartender.do_some_bar_stuff

# friday night
bartender = Bartender.new(NotQuiteAsBoringSportsBar.new)
bartender.do_some_bar_stuff
有帮助吗?

解决方案

我认为你所触及的本质区别是:

  • 语言组 1.例如,当调用 object.method1、object.method2、object.method3 时,实际调用的方法可能会在对象的生命周期内发生变化。

  • 语言组 2.例如,当调用 object.method1、object.method2、object.method3 时,所调用的实际方法在对象的生命周期内不能更改。

第 1 组中的语言倾向于具有动态类型并且不支持编译时检查接口,而第 2 组中的语言倾向于具有静态类型并且支持编译时检查接口。

我想说所有面向对象的原则都适用于两者,但是

  • 第 1 组中可能需要一些额外(显式)编码来实现(运行时而不是编译时)检查,以断言新对象是使用所有适当的方法创建的,以满足接口契约,因为没有编译时接口协议检查,(如果你想让第 1 组代码更像第 2 组代码)

  • 第 2 组中可能需要一些额外的编码,以通过使用额外的状态标志来调用子方法来对方法调用所调用的实际方法的更改进行建模,或者将方法或一组方法包装在对附加的几个对象之一的引用中到主对象,其中每个对象都有不同的方法实现,(如果你想让第 2 组代码更像第 1 组代码)

  • 第 2 组语言对设计的严格限制使它们更适合大型项目,在这些项目中,易于沟通(而不是理解)变得更加重要

  • 第 1 组语言中缺乏设计限制,因此更适合较小的项目,程序员可以更轻松地一目了然地检查是否满足各种设计管道约束,因为代码更小

  • 使用一组语言像另一组语言一样编写代码很有趣并且非常值得研究,但是语言差异的关键在于它们如何帮助不同规模的团队(-我相信!:) )

  • 还有其他各种差异

  • 用一种语言或另一种语言实现面向对象设计可能需要或多或少的跑腿工作,具体取决于所涉及的具体原则。


编辑

所以为了回答你原来的问题,我检查了

http://c2.com/cgi/wiki?PrinciplesOfObjectOrientedDesign

http://www.dofactory.com/patterns/Patterns.aspx

在实践中,系统中由于各种好的原因(当然也有一些不好的原因)而没有遵循面向对象原则。充分的理由包括,性能问题超过了纯粹的设计质量问题,替代结构/命名的文化利益超过了纯粹的设计质量问题,以及非以特定语言的标准方式实现功能的额外工作成本超过了以下好处:纯粹的设计。

诸如抽象工厂、构建器、工厂方法、原型、适配器、策略、命令链、桥接器、代理、观察者、访问者甚至 MVC/MMVM 之类的粗粒度模式往往在小型系统中使用较少,因为有关代码较少,因此创建此类结构的好处就不那么大了。

更细粒度的模式,如状态、命令、工厂方法、组合、装饰器、外观、享元、备忘录、模板方法可能在第 1 组代码中更常见,但通常有几种设计模式不适用于对象本身,而是适用于对象的不同部分。一个对象,而在第 2 组中,代码模式往往以每个对象一个模式的形式出现。

恕我直言,在大多数第 1 组语言中,将所有全局数据和函数视为一种单例“应用程序”对象是很有意义的。我知道我们正在模糊过程编程和面向对象编程之间的界限,但在很多情况下,这种代码肯定像“应用程序”对象一样嘎嘎叫!:)

一些非常细粒度的设计模式(例如迭代器)往往内置于第 1 组语言中。

其他提示

让我说开始,就个人而言,这并不在这两个动态和静态类型语言的工作原理OOP不是原则。

这就是说,这里是一个例子:

在接口分离原则( http://objectmentor.com/resources/articles/isp。 PDF )指出,客户应取决于满足他们需求的最具体的接口上。如果客户端代码需要使用C类的两个方法,那么C应该实现的接口,我只包含了这两种方法,客户端将使用我,而不是C.这个原则是在不需要接口,动态类型语言无关的(因为接口定义的类型,并且在变量是类型更小的语言)

不需要类型

[编辑]

第二实施例 - 依赖倒置原则( http://objectmentor.com/resources/articles /dip.pdf )。这一原则认为是“而不是在具体的功能和类取决于接口或抽象的函数和类的策略”。同样,在动态类型语言的客户端代码不依赖于任何东西 - 它只是指定的方法签名 - 从而避免了这一原则。

第三示例 - 里氏替换原则( http://objectmentor.com/resources/articles/ lsp.pdf )。这条原则的教科书的例子是一个Square类的子类一个Rectangle类。然后可调用setWidth()方法上的一个矩形变量客户机代码是惊讶当高度也改变,因为实际的对象是正方形。再次,在一个动态类型语言中的变量是类型少,Rectangle类不会被在客户端代码提及,因此没有这样的意外,可能不会出现。

我有这一切“激进”的观点:在我看来,用数学的支持,OOP不会在任何有趣的问题是静态类型的环境中工作。我定义有趣的含义抽象关系都参与其中。这可以很容易地证明(见“协方差问题”)。

这个问题的核心是面向对象的概念保证这是模型抽象并结合由静态类型提供的合同编程,不能在不破坏封装中实现关系的方式。刚刚尝试任何协二元操作者看到:尝试实施“低于”或C“加上” ++。您可以轻松地编写基本抽象,但你不能执行它。

在动态系统没有高级别形式化类型和无封装有这么OO实际工作,特别地,基于原型像原来的Smalltalk系统实际交付不能在所有的静态类型约束进行编码工作模式。打扰

要回答这个问题的另一种方式:非常问题的基本假设是instrinsically缺陷。 OO没有任何连贯的原则,因为它不是一个一致的理论,因为那里不存在的任何车型足够的权力来处理任何事情,但简单的编程任务。所不同的是你放弃的东西:在你放弃封装动态系统,静态系统,您只需切换到模型做的工作(功能编程,模板等),因为所有的静态类型的系统都支持这些东西。

界面可以添加开销一定程度上,特别是如果你直接依赖于别人的API。简单的解决方案 - 不依赖于别人的API

让每个对象谈话说,希望在一个理想的世界会存在的接口。如果你这样做,你会与有小范围内的小接口告终。通过这样做,当接口改变,你会获得编译时失败。

在更小,更具体的接口,少“记账”,你就必须做一个接口的改变。

一个静态类型的真正好处不是静态知道什么方法可以打电话,但低保值对象已经验证...如果你需要一个名字,和名字必须是<10个字符,创建名称类,封装了验证。(虽然不一定任何I / O方面 - 保持一个纯粹的数值型),编译器可以帮助您在编译时捕获错误,而不是你需要验证在运行时

如果你打算使用一个静态的语言,用它来你的优势。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top