2011-08-27 67 views
2

我对使用具体类和接口的影响有一些疑问。OO原则:c#:设计为接口而不是具体类

  1. 说的代码(称之为chunkCode)一些大块使用具体类A。我是否需要重新编译chunkCode如果:

    1. 我在A中添加了一些新的公共方法?如果是这样,那是不是有点道理?毕竟我仍然提供接口chunkCode依靠。 (或者我必须重新编译,因为chunkCode可能永远不会知道这是真的,我没有省略一些API)
    2. 我添加一些新的私有方法到A
    3. 我为A添加了一个新的公共字段?
    4. 我添加一个新的私人领域A
  2. 工厂设计模式: 主代码并不在乎对象的具体类型是什么。它仅依赖于API。但是如果只有几种方法只与一种具体类型相关,你会怎么做?这种类型实现了接口,但增加了更多的公共方法?你会使用一些if (A is type1)语句(或类似的)的主要代码?

感谢任何澄清

+0

你可以添加一些示例代码?一般来说,如果一个接口没有改变,你将不需要重新编译,但是如果你正在对付具体的类,你会的。 – davecoulter

+1

哈,大家都在编辑这篇文章。我更喜欢项目列表... – celavek

回答

0

关于第一个问题的答案是否定的所有点。如果是这样,那么向后兼容性就没有任何意义。只有在制动API时,你必须重新编译chunkCode,即删除chunkCode正在使用的某些功能,更改调用约定,修改参数数量,这些事情==打破更改。

对于第二个我通常,但只有当我真的必须在这些情况下使用dynamic_cast。我的答案在C++环境中是有效的;我刚刚看到问题是语言不可知(在这个小时里有一种疲倦;如果它攻击任何人,我会删除答案)。

+0

你会如何回应0verbose的答案? “如果chunkCode直接引用一个具体类是” –

+1

@Elad Benda在C++的情况下,我认为我的答案是成立的。就Java而言,它也适用 - 在Java中,即使在添加到接口的情况下更改接口,也不必重新编译(请参阅http://stackoverflow.com/questions/6780899/do-clients -have-to-recompile-after-changing-a-java-interface;在接受的答案中提供的链接是一个很好的阅读)。有时候,在边缘情况下,你确实需要重新编译,参见http://stackoverflow.com/questions/536971/do-i-have-to-recompile-my-application-when-i-upgrade-a-third-party -罐。 – celavek

0

问题1:取决于你在说什么语言。它总是比较安全的重新编译两种语言。主要是因为chuckCode不知道A中实际存在的内容。重新编译刷新其内存。但它应该在没有重新编译的情况下在Java中工作。

问题2:编号写一个工厂的整个要点是摆脱如果(A是type1)。从维护的角度来看,这些陈述是可怕的。

工厂的目的是建立类似的对象。如果您遇到了使用此语句的情况,那么该对象要么与类的其余部分不是类似的类型。如果您确定其类型相似,并且界面相似。我会在所有具体的基类中编写一个额外的函数,并只在这个函数中实现它。

理想情况所有这些具体类都应该有一个共同的抽象基类或一个接口来定义API的内容。除非您正在编写采用此特定类的函数,否则除了此接口中设计的内容外,应该预期在代码中的任何位置调用该函数。

+0

“重新编译刷新其内存”。重新编译可能意味着清理上次编译的输出并重新编译,而不是刷新内存。 –

+0

我不是指物理内存。我的意思是它将重新建立班级和功能位置。如果界面改变,这是特别必要的。你至少得到一个编译错误。 Java可能不是这样,但如果你看看C++,函数的实际位置就存储在代码中。 – arunmur

4

1)编译不是面向对象的一项活动。它是特定OO实现的细节。如果你想要一个特定的实现(例如Java)的答案,那么你需要澄清。

一般来说,有人会说增加一个接口不会被认为是一个重大改变,其他人说你发布后你不能改变接口,你必须创建一个新的接口。

编辑:您指定了C#,因此请检查this question regarding breaking changes in .Net。我不希望这样做会造成伤害,所以我不会在这里复制它。

2)人们经常破解他们的设计来做到这一点,但这是一个标志,你有一个糟糕的设计。

很好的替代

  • 创建界面中的任何一种方法,使您可以调用自定义的行为,但并不需要知道行为是什么。

  • 创建支持新方法的附加接口(和新工厂)。新接口不必继承旧接口,但如果有意义(如果接口之间可以表示关系),则可以继承旧接口。

  • 如果您的语言支持它,请使用Abstract Factory pattern,并在混凝土工厂中利用Covariant Return Types。如果您需要特定的派生类型,请接受一个具体的工厂而不是抽象的工厂。

不好的选择(反模式):

  • 添加到,什么也不做其他接口的方法导出归类。

  • 在对派生类没有意义的方法中抛出异常。

  • 向接口添加查询方法,告诉用户他们是否可以调用某个方法。

除非方法名称是通用的,以至于用户不会指望它做任何事情(如DoExtraProcessing),然后补充说,是无操作的最派生类的方法打破了由该接口定义的合同。

例如:有人调用bird.Fly()会期望它实际做某事。我们知道鸡不能飞。因此,Chicken不是BirdBird不是Fly

添加查询方法对此很糟糕。例如。在界面中添加boolean CanFly()方法或属性。所以抛出异常。他们都没有解决类型根本不可替代的事实。检查Liskov Substitution Principle(LSP)。

+1

+1:很好的解释 – Heisenbug

+0

上面列出的反模式的一个值得注意的例外是[Freezable Pattern](http://msdn.microsoft.com/zh-cn/library/ms602734.aspx)。在这种情况下,您需要确保您返回的接口/基类是可冻结的,并且工厂的文档声明它总是返回冻结或解冻的实例。 –

+0

1)我指的是c#。那么谁是对的(关于C#):0verbose还是celavek? –