我对使用具体类和接口的影响有一些疑问。
说一些代码(称之为chunkCode
)使用具体的类A
。如果符合以下条件,我是否需要重新编译chunkCode
A
添加了一些新的公开方法?如果是这样,是不是有点st?毕竟我仍然提供接口chunkCode
所依赖的。 (或者我必须重新编译,因为chunkCode可能永远不会知道这是真的,我没有省略某些API)A
添加了一些新的私有方法?A
添加了一个新的公共字段?A
添加了一个新的私人字段?工厂设计模式:
主代码不关心对象的具体类型。它仅依赖于API。但是,如果只有少数方法只与一种具体类型相关,那么你会怎么做?这种类型实现了接口,但增加了一些公共方法?你会使用一些if (A is type1)
语句(或类似的)主代码吗?
感谢您的任何澄清
答案 0 :(得分:4)
1)编译不是OO中的活动。它是特定OO实现的细节。如果您想要特定实现的答案(例如Java),那么您需要澄清。
一般来说,有人会说添加到界面不会被认为是一个重大变化,其他人说,一旦发布界面就无法更改界面,并且你必须创建一个新的界面。
修改:您指定了C#,因此请查看this question regarding breaking changes in .Net。我不想那样做是为了解决这个问题,所以我不打算在这里复制它。
2)人们经常破解他们的设计,但这表明你的设计很糟糕。
好的选择:
在界面中创建一个方法,允许您调用自定义行为,但不需要知道该行为是什么。
创建支持新方法的附加接口(和新工厂)。新接口不必继承旧接口,但如果有意义则可以(如果接口之间可以表达is-a关系)。
如果您的语言支持,请使用Abstract Factory pattern,并在具体工厂中利用Covariant Return Types。如果您需要特定的派生类型,请接受具体工厂而不是抽象工厂。
不良选择(反模式):
在接口中添加一个方法,该方法在其他派生类中没有任何作用。
在对派生类没有意义的方法中抛出异常。
向界面添加查询方法,告诉用户是否可以调用某种方法。
除非方法名称足够通用,用户不希望它做任何事情(例如DoExtraProcessing
),否则在大多数派生类中添加no-op的方法会破坏该接口定义的契约
例如:某人调用bird.Fly()
会期望它实际上做某事。我们知道鸡不能飞。因此,Chicken
不是Bird
,或Bird
不是Fly
。
添加查询方法对此来说是一种糟糕的解决方法。例如。在界面中添加boolean CanFly()
方法或属性。所以抛出异常。他们都没有解决这个类型根本不可替代的事实。查看Liskov Substitution Principle(LSP)。
答案 1 :(得分:0)
对于你的第一个问题,答案是否定的。如果它会那样,那么向后兼容性没有任何意义。只有在制动API时才需要重新编译chunkCode,即删除chunkCode
正在使用的某些功能,更改调用约定,修改参数数量,这些事情= =破坏更改。
对于第二个我通常,但只有在我真的必须的时候,在这些情况下使用dynamic_cast
。
注意我的回答在C ++的上下文中是有效的;我刚看到这个问题与语言无关(在这个时候有点累;如果有人攻击任何人,我会删除答案)。< / p>
答案 2 :(得分:0)
问题1:取决于您所使用的语言。尽管重新编译这两种语言总是更安全。主要是因为chuckCode不知道A里面究竟存在什么。重新编译会刷新它的内存。但它应该在Java中工作而无需重新编译。
问题2:否。编写工厂的整个要点是摆脱if(A是type1)。从维护角度来看,这些if语句很糟糕。
工厂旨在构建类似类型的对象。如果您遇到使用此语句的情况,则该对象与其他类的类型不同。如果您确定它的类型相似并且具有类似的接口。我会在所有具体的基类中编写一个额外的函数,并仅在此基础上实现它。
理想情况下,所有这些具体类都应该有一个共同的抽象基类或一个接口来定义API。除非您正在编写具有此特定类的函数,否则除了您正在编写具有此特定类的函数之外,不应在代码中的任何位置调用此接口中设计的内容。