是否有关于此类编程的一些文献?

时间:2010-08-05 14:25:22

标签: design-patterns oop functional-programming

在大学里,我参加了一个关于现代物理学的课程,在那里我们学习了狭义相对论。我完全被不同的参考框架如何实际观察到物体的物理属性变得不同而且都不正确。随着时间的推移,这个概念慢慢地改变了我编程的方式,现在我倾向于将类分为2个主要类别,即数据对象和观察(仅函数)对象。

为了解决这个问题,我不会成为这么简单问题的详细而冗长的帖子,我会尝试用两个例子来解释我的意思。

首先,以我曾经常写的这种类型的代码为例:

class Deck
{
    private Card[] cards;

    public void Shuffle()
    {
        // shuffle algorithm
    }

    // other deck related functions like draw, cut, etc...
}

我现在通常编写相同的场景:

class Deck
{
    // by intention, i just mean some kind of immutable collection of cards
    private ReadonlyCollection<Card> _Cards;

    public Card[] Cards { get { return this._Cards; } }
}

interface IDeckHandler
{
    Deck Shuffle(Deck deck);
    // other deck related functions like draw, cut, etc..
}

class Dealer : IDeckHandler
{
    // IDeckHandler implementation
}

Deck不再负责实施可以对其采取行动的职能。或者为了使其符合术语,甲板只是一组值,观察它的方式是观察者的责任。当然,可能有许多观察者以不同的方式执行这些行动。

对于第二个例子,我将使用我尝试解释过这个的人可以更轻松地使用的东西。假设我们在彩色纸上有彩色字母拼出一个单词。我们有一个代理人,负责阅读报纸上的文字。现在假设代理是某种类型的色盲。从纸张发出的图像是相同的,但感知可能是不同的。观察者对对象没有深入了解,也无法对其进行修改,只能对其进行解释。

正如我所说,这个概念推动了我的许多发展决策。回到这个问题,这是一种已发布的编程类型,如果是这样,你能指点一些关于它的文献吗?我遇到了一些常见且不常见的情况,这些情况很难做出决定,当然还有一些我还没想过或碰到过的东西,希望这些东西可以在文献中进行检验。

8 个答案:

答案 0 :(得分:10)

在我看来,您正在以OOP方式实现函数式编程。 无论物理学对“狭义相对论”的解释如何,OOP的整个概念基本上都是封装 - 你想让对象知道自己应该怎么做。基本上你在这里说的是“不,只有数据和函数可用于数据”。如果甲板改变怎么办?你有一个新的经销商?您如何知道应该创建哪个经销商来处理新套牌?

如果您考虑过“切换”语句,那么您就是在将OOP概念变成功能性编程模板。

答案 1 :(得分:6)

这种编程风格的某些方面包含在面向数据的编程(一种专注于数据布局和转换的开发风格)中。

我对这种风格的主要问题是,如果给定类型存在隐含的假设/约束(例如,卡片组在洗牌后连续不得有2个连接器),则必须在整个过程中复制这些约束/检查所有管理器类型,因为你正在操作的数据类型是完全愚蠢的 - 它不能照顾自己,它只是一个数据包。您可以将重复的逻辑提取到单独的方法中,但使用此方法编写好的,干净的代码通常要困难一些。

将此与实施采用Deck.Shuffle策略的IDeckShuffle方法进行比较。在这种情况下,您可以执行随机播放,然后添加不变检查作为后续步骤,以确保无论使用什么shuffle策略,卡片组都不会进入无效状态;强制完整性的代码在一个地方,易于验证和更新。

此外,由于对IDeckShuffler.Shuffle(...)的调用来自Deck内部,因此该套牌可以访问所有隐藏字段和封装状态。因此,您可以将最小详细信息公开给deck shuffler实现,而不是默认传递Deck。相反,您可以传递IEnumerable<Card>或更不具体的内容,而不是默认传递整个数据包。

无论如何,您正在询问的开发形式基本上是程序编程。因此,隐藏信息和封装事物更加困难。在性能关键系统中,这可以是可接受的权衡(按类型对所有数据进行分组,然后使用管理器'进程'函数=良好的高速缓存一致性来迭代它)。

在一般开发中,我远离这种编程风格,因为它严重阻碍了我管理复杂性的能力。 Ayende had a good post about this a while back。虽然他在谈论数据存储对象及其上的操作,但原理完全相同 - 数据与作用于该数据的函数及其中的问题的分离。

答案 2 :(得分:5)

您正在做的是将概念的数据与作用于该概念的操作分开。系统 的系统是什么。这为许多不同的场景打开了大门,在这些场景中,您可以使用不同的行为类来更改系统的行为。这些行为类也可以重用于不同的数据类。许多模式解决了这个问题,如访问者,命令,策略或观察者。

但这里有更深层次的东西。也许我们需要在我们的(主流)编程语言中(或者只是在我们的脑海中)使用另一个概念,这将允许我们分离和重用这些行为。

DCI architecture解决了这些问题,并将角色或traits (pdf)作为行为和代码重用的基本单位。

答案 3 :(得分:5)

这是大多数应用程序布局的典型方式。我认为类是形成二分法 - 数据容器对象和策略对象。数据容器对象是信息的载体,而策略是可以应用于数据容器的各种算法的封装。

命令模式非常接近策略模式。策略也倾向于表现为控制者,外墙等。

数据容器对象显示为实体,模型对象,值对象,数据传输对象等。

四人帮是一个良好的开端,但您可能希望研究其他经典设计模式论文以进一步阐述。根据您的编程语言偏好,您可能还需要考虑更具体的模式书籍。亚马逊有很多关于设计模式的清单。

我曾在this article谈过阶级二分法。

答案 4 :(得分:4)

有趣。好吧,我认为你在做Model-&gt; Controller-&gt; View(MVC Pattern),但在这种情况下你只是单独使用Controller和Model部分。

如果您有多个使用对象的场景,这里的收益很明显,这是典型的POJO + Managers做事方式。在这种情况下,对象本身是愚蠢的,除了自己的状态之外没有任何功能。

在责任分离方面的优势很明显,尽管缺点是对经理方面的处理更多。

如果你考虑一下,如果对象不对自己做任何事情,你基本上会削减一个间接级别(基本级别),所有东西都必须被指向使用。这意味着需要更多代码来处理意外情况,null对象等。可能比需要更多的胶水代码。

灵活?是。实际的?只有你可以回答。

答案 5 :(得分:4)

此设计模式可以很好地工作,特别是如果操作不会改变原始数据对象。例如,在.NET中,LINQ及其相关的扩展方法可以看作是处理枚举的一般操作,因此枚举本身不需要知道如何使用它们。但是,这些方法不会改变枚举的集合,而只是提供一种新的方法来解释,过滤和分组枚举。

但是,如果功能正在改变原始对象,那么我倾向于将该功能封装为对象上的方法。这样,对象负责维护自己的不变量,并从对象的客户端隔离更多的实现细节。为了使用该对象,您必须泄漏的实现细节越多,就越有机会错误地使用它并导致错误。

答案 6 :(得分:1)

你所谈论的是一个伟大的“HA!”面向对象程序员遇到的时刻。它发生时很有趣。我将从四个“设计模式”一书开始,并从那里开始分支。

答案 7 :(得分:0)

在Java世界中,我们拥有很好的容器,包含无状态会话bean,可以精确地用于将行为与数据分离,正如DCI架构所规范的那样。这有时被称为面向服务的编程。

OOP通过要求将行为放置在数据所在的类中来约束设计者,以便增加一致性,而不是让设计者将相关数据放在一起,并将相关行为放在一起,但不一定也将行为推向那些行为数据类。

在一个好的设计中,我们有时会在类中有行为,有时我们会在更高阶的类中有行为。