我面临的设计决定对我来说没有味道,但让我停下来。看一下下面的代码示例:
public interface IGenerator
{
///<summary>
/// Combines two generators; performs magic as well
/// </summary>
BaseGenerator Combine(BaseGenerator next);
///<summary>
/// Does what a generator does.
/// </summary>
object GenerateLolKThx();
}
public abstract class BaseGenerator : IGenerator
{
///<summary>
/// Combines two generators; performs magic as well
/// </summary>
public BaseGenerator Combine(BaseGenerator next)
{
// do stuff that I really don't want implementors to try and do
// because its complex and can result in bad juju if done wrong
return SuperSecretCombine(this, next);
}
///<summary>
/// Does what a generator does.
/// </summary>
public abstract object GenerateLolKThx();
/* other base class methods */
}
我不想详细了解为什么我不想信任使用Combine方法的实现者;足以说它的复杂性。但是,我确实希望尽力强制任何想要实现IGenerator的人扩展BaseGenerator,因为这是正确组合两个生成器的唯一方法。这是由界面本身强制执行的。
我担心由于我在该界面中引用接口的实现而导致意外问题(由“气味”表示)。但是我也知道这种事情在CS中并不是闻所未闻,它本身也不是坏事(即描述XSD和用他们编译的语言编写的语言编译器的XML模式)。
答案 0 :(得分:14)
我感兴趣的原因为什么这可能是代码味道
这是我的答案:将接口与实现分离的目的之一(甚至可能是主要目的之一)是为接口创建许多不同的实现。将接口绑定到特定的实现类会破坏这一点。
您实际上是在说 - “此接口必须由BaseGenerator或其子类实现”,但为什么要将IGenerator
与BaseGenerator
分开?
答案 1 :(得分:6)
可以用扩展方法来完成吗?然后你可以使用界面。扩展方法是向接口添加通用实现逻辑的好方法。
这最适合您主要谈论IGenerator
(不具体类型) - 因为扩展解析更喜欢类型 - 即FredsGenerator
可以声明定制Combine
方法,将(如果您键入变量为FredsGenerator
)优先。但这与您的示例没有什么不同,因为FredsGenerator
可以重新实现接口,从而窃取实现:
using System;
interface IFoo {
void Bar();
}
abstract class FooBase {
public void Bar() { Console.WriteLine("Non-virtual; you can't override me!!!"); }
}
class FooImpl : FooBase, IFoo {
new public void Bar() { Console.WriteLine("mwahahahahah"); }
}
static class Program {
static void Main() {
IFoo foo = new FooImpl();
foo.Bar();
}
}
至少使用扩展方法方法你的代码不能被愚弄到运行重新实现的版本;您的代码只知道IGenerator
,因此仅使用 Generator.Combine
版本。如初。
示例(编辑从您的示例中重新设计):
using System;
public interface IGenerator
{
// note: no Combine
object GenerateLolKThx();
}
public static class Generator
{
///<summary>
/// Combines two generators; performs magic as well
/// </summary>
public static IGenerator Combine(this IGenerator current, IGenerator next)
{
// do stuff that I really don't want implementors to try and do
// because its complex and can result in bad juju if done wrong
Console.WriteLine("Super secret logic here...");
// and prove we have access the the objects...
Console.WriteLine(current.GenerateLolKThx());
Console.WriteLine(next.GenerateLolKThx());
return next; // just for illustration
}
}
class MyGenerator : IGenerator
{
private readonly object state;
public MyGenerator(object state) {this.state = state;}
public object GenerateLolKThx() { return state; }
}
public static class Program
{
static void Main()
{
IGenerator foo = new MyGenerator("Hello"),
bar = new MyGenerator("world");
IGenerator combined = foo.Combine(bar);
}
}
答案 2 :(得分:6)
根据您显示的有限代码,我认为您正在使用接口只是为了它,抽象基类更合适。如果你不相信任何其他人合并BaseGenerators,那么它应该是你的层次结构的基础,而且Combine不应该是虚拟的,所以没有人可以覆盖它。
答案 3 :(得分:4)
BaseGenerator(及其子类)是唯一实现IGenerator接口的类吗?
[这看起来像一个问题而不是答案,但它的意思是苏格拉底式。]
答案 4 :(得分:2)
为什么不参考界面?
public interface IGenerator
{
///<summary>
/// Combines two generators; performs magic as well
/// </summary>
IGenerator Combine(IGenerator next);
///<summary>
/// Does what a generator does.
/// </summary>
object GenerateLolKThx();
}
答案 5 :(得分:0)
如果有多个继承可用,听起来整个事情可以更好地实现。如果我是对的,这里没有代码味道。如果我错了,可能会有奇怪的代码味道,因为该接口与其中一个实现紧密耦合。