代码之家  ›  专栏  ›  技术社区  ›  Tom W

开放式问题/思考哲学

  •  0
  • Tom W  · 技术社区  · 14 年前

    我将利用这个机会来发表一些关于面向对象哲学的相关但不同的想法,作为一种征求意见的方式。如果管理员认为可以关闭,我可以把它改为博客文章。但是我在问问题,所以我认为这是一个合适的社区wiki。

    假设我有一个 abstract class Bird . 假设我们在这里采用了一种相当典型的OO哲学, Bird 是一个能够保持和改变状态的对象。这个简单模型的一个明显的例子是 是飞还是不飞。

    所以, 有方法吗 fly()

    还是真的?我在介绍性的书中看到了这个非常简单的例子,在我看来,这是一个微妙的误导。如果要维护状态,则不需要告诉对象 继续 在一个给定的状态下;它就是。这就是物体的作用。所以真的,你想 有办法 takeoff() .

    假设在某个时刻我们创造了 来自另一个类的对象, Egg . 在某种程度上,我们可以调用一个方法 Egg.hatch() 具有返回类型 . 假设我们 编码到接口 , 鸡蛋 hatch() 也知道它是哪种鸟。但我们不能说 鸡蛋 还有一个 从中孵化出来的。我们真的是想让这些类的具体实现引用某种 BirdArchetype 代表了该物种所有实例的特征,因此 鸡蛋 了解自己的同类,独立但始终如一?这种关系有什么众所周知的模式吗?

    是谁的责任来确保卵子改变状态,使它不再能够再孵化,或者做其他大多数鸡蛋通常会做的事情?

    从哲学上讲,我们是否要引入 易碎的 反对?也许 鸡蛋 应该进行编码,以便在尝试执行任何多次更改其状态的操作时引发异常。由调用代码来跟踪客户机对象的生命周期。

    cook() 一个鸡蛋“打破”了 鸡蛋 实例并返回新对象的实例, FriedEgg . 这和告诉别人有什么不同 起飞() ? 认为起飞使它成为一只完全不同的鸟是荒谬的。然而道理是一样的:一只正在飞行的鸟(通常)不能做其他鸟做的大部分事情,因为它很忙。那么可以说,

    我想这里唯一真正的区别是 可以变成 联合国 -坏了。热力学可逆性的概念真的对这样的简单模型编程那么重要吗?

    6 回复  |  直到 14 年前
        1
  •  2
  •   Joshua    14 年前

    这就是为什么我不特别喜欢对真实世界建模并用它来解释/定义OO哲学。

    除非你正在构建一个模拟器(在这个模拟器中,当你看到模拟器的上下文时,这些问题的答案会立即变得很明显),否则你很容易陷入困境并迷惑自己。

        2
  •  2
  •   user166390 user166390    14 年前

    也许在对象中具有突变状态是完全错误的;-)

    不应该 Bird.takeoff() FlyingBird 哪一个可以 land() ? (对于一只不飞到陆地上的鸟来说,这显然是没有意义的。)

    同样,对于鸡蛋:

    egg = Egg()
    incubatedEgg = egg.incubate()
    bird = incubatedEgg.hatch()
    

    egg = Egg()
    egg.incubate() ->
     if "still alive" (aliveEgg) ->
       aliveEgg.hatch()
     else "died in incubation" (deadEgg) ->
       deadEgg.discard()
    

    快乐哲学化:-)

        3
  •  2
  •   Sir Robert    14 年前
    1. “飞”合理的意思是“开始飞行!”(我想说是用过的 在自然语言中)。你不会的 把它解释为“持续飞行!”这就是为什么我们合理地使用“fly”和“land”(在自然英语中)作为反义词。( Evidence for the skeptical

    2. 你假设这个蛋是被创造出来的 前尼希洛 在你的模型里。这不是一个合理的假设。鸟是从蛋里出来的,但蛋是从鸟身上出来的。假设对一个卵子进行建模的目的是合理的,那就是对鸟类的延迟繁殖进行建模。这意味着幼鸟是由另外两只鸟的有性繁殖形成的。雏鸟世代的载体是蛋。鸡蛋不需要知道里面是什么鸟;那只鸟是由父母创造的,包裹在一个普通的鸡蛋里(根据母亲的不同,它可能有不同的属性)。

      换句话说,你的模型在这个问题上并不是很好。

    3. 鸡蛋有责任确保它不会再次孵化(检查孵化功能)。见下文关于“必要性”的讨论。

    4. “易碎”的概念对于您的模型可能是多余的。你关心的是鸡蛋的易碎性,还是孵化能力?如果中断是您试图建模的数据的重要部分(例如,如果您的模型中有任何内容 基本上取决于 在鸡蛋破裂后,再做模型。孵化的时候,鸡蛋也会破壳而出。其中之一 后果 孵化正在破裂,而不是相反。

    5. 记住,编程语言是 语言;询问、劝诫和其他“自然语言”功能在编程语言中本质上是错误的,即使我们通过命令实现了它的某些版本(我们通过命令模拟询问;“告诉我你的状态!”是对疑问句“你的状态是什么?”)的强制性解释吗

    6. 好节目 删除尽可能多的信息 满足所有功能规格 . 也就是说,您有一个规范(最好是以测试套件的形式)。无意义的信息就是不存在的信息 必要的 通过任何测试。因此,您一次只实现一个特性,当您有一个通过规范量化该特性的测试时,引入了鸡蛋的可烹调性(再次阅读:tests)。如果你实现了对象的可破坏性,并且意识到你可以在没有它的情况下完成同样的测试, 你可以通过删除它来改进程序 . 现在,一个真正聪明的程序员能够以这样的方式构建他的模型 可扩展 --也就是说,当产生新的需求规范时,可以很容易地添加新的属性。

      另外请注意,也可能有一个错误的规范。如果你不能判断某些东西是不是多余的,说明(读:测试)要么是不正确的,要么是不完整的。

        4
  •  0
  •   voodoogiant    14 年前

    似乎更像是一个命名问题。例如,在java中,您可以将bird的状态查询为“getFly()”,在这里可以像python一样将其称为“fly()”。在这里,你告诉它起飞就像你在改变鸟的状态,这对代码的读者来说有点模棱两可。

    以鸡蛋为例,我不认为鸡蛋处理的鸟类作为蛋的一部分有任何问题。至于由谁来处理蛋是否孵化,你只需在蛋内保留一个孵化鸟的参考。一个鸡蛋从身体上孵化出来,所以我要看它是否孵化了,所以我不会把蛋壳放在保温箱里几个月,期待着一只鸡突然冒出来。在我看来,要知道是谁孵化了,就要看召唤密码了。如果你的计划有助于把鸟蛋放在里面的话。我认为这对任何使用该类的人都是一种方便。

    没有理由不让鸡蛋也处于油炸状态,任何孵化的尝试都会失败。

    至于你的鸟在飞行中会碎,那是鸟的错吗?如果我的狗在狗窝里,如果我叫它去拿球,它会不会坏掉?

        5
  •  0
  •   DVK    14 年前

    这取决于你是想把鸡蛋留在模型里还是考虑鸡蛋不见了。

    如果是前者 hatch() 方法设置私有 hatched 标记为true,以及依赖于未孵化的蛋的任何方法(包括 图案填充() 本身)检查标志和失败(失败是通过返回代码还是引发的异常,取决于)。后者,可能在某些设计上可行,但并不真的认为有任何必要。

    我认为你这里的问题是不精确的单词用法,而不是软件开发。作者 fly() 解决方法是usng“fly”动词的意思是“start flight”,例如作为“takeoff”的同义词。不管这是不是100%有效的英语用法,都高于我作为ESL开发人员的薪酬级别,但我肯定同意“takeoff”是一个不那么含糊的方法名。

    ... 所以鸡蛋和鸟都能独立但始终如一地认识自己的同类?这种关系有什么众所周知的模式吗?

    我们还能用鸡蛋做什么?也许我们可以做。如果是我,我会编写这样的代码,以便对一个egg调用cook()会“破坏”egg实例,并返回一个新对象的实例FriedEgg。这和叫鸟起飞有何不同()

    正如一位评论者已经指出的,上面提到的是一个面向对象的设计问题——鸡蛋不会自己煮。因此,您不能孤立地对egg实现“cook”方法(尽管您的模型可能需要一些“setstatus to cooked”方法,如果没有其他原因,只会使其不可匹配。然而,FriedEgg的东西需要由一个Cook对象的“Cook峎egg”方法构造(如果需要的话),这个方法也在一个鸡蛋上调用“set status to coiled”。

        6
  •  0
  •   Vivin Paliath    14 年前

    我不认为这符合OO哲学。在我看来,你把业务逻辑和设计模式混为一谈。

    如果有一个方法可以更改对象的状态,这可能会导致 另一个 方法要基于此新的(已更改的)状态引发异常,最好使用状态检查方法,这样就不必每次都显式地处理异常。

    在你的情况下,假设你做了 egg.fry() (另一方面,鸡蛋不能自己煎。所以也许你需要一个 Cook 需要一个 egg fry 方法并返回 FriedEgg 在那之后你做到了 egg.hatch() . 第二个调用必须返回异常。而不是强制类的用户将该调用放入显式 try...catch 块,您应该提供一个状态检查方法;类似于 isFried() isHatchable() . 而不是:

    try {
      egg.hatch();
    }
    
    catch(UnhatchableException e) {
      ...
    }
    

    你有:

    if(egg.isHatchable()) {
       egg.hatch();
    }
    

    因此,调用代码的责任是在执行可以 抛出异常。

    现在假设你有 厨师 类和一个名为 油炸 你做到了 Cook.fry(egg) 返回一个 弗里代克 例如,如果你打电话会怎么样 hatch() 在上面?常识告诉你,煎蛋不能孵化任何东西!

    在这种情况下,你应该有一个 Egg 接口 LiveEgg 弗里代克 两者都实现了 鸡蛋 接口。但是,不同之处在于 图案填充() 方法 它必须抛出一个 UnsupportedOperationException 因为你不能孵化一个煎蛋。

    建模大多数真实场景(如汽车、动物等)的问题在于,有时它们无法帮助充分解释关系。这是因为OO概念非常抽象。

    不管怎样,希望这有帮助。