混凝土类型或界面?

问题描述:

我有以下用例,很多代码紧密耦合在一个具体类型上(比如Concrete1)。后来想出了具体的类型需要改变,所以定义了一个接口。例如混凝土类型或界面?

Class ABC { 

virtual int foo() = 0; 
virtual int getType() = 0; 

} 

class Concrete1 : public ABC { 

    int foo() { 
    ... } 
    int getType() { 
     return 1; 
    } 

} 
class Concrete2 : public ABC { 
    int foo() { 
    ... } 
    int getType() { 
     return 2; 
    } 
} 

静态工厂模式用于创建对象。因此,创建对象新建Concrete1的所有地方都被替换为ABCFactory :: createType()。

现在代码中有很多地方需要检查createType返回的对象是否是Concrete1或者Concrete2并相应地执行相关逻辑(因此很多代码中的其他内容:():

我想避免在代码中有很多的if else作为这种变化的一部分。有什么建议?

这困扰了我很多的事情是

if (abc.getType() == 1) { 
    ... 
} else if (abc.getType() ==2) { 
    ... 
} 

使用接口的全部要点是,您可以使用多态,这意味着您不必检查实例是什么类型。这样做是一个非常大的代码味道(请参阅Fowlers Refacotring)。移动条件逻辑的具体类,并添加TE功能,将它处理到接口

编辑(添加的代码示例,因为最初的职位是由手机完成):

你正在尝试做的:

void Main(string[] args) 
{ 
    Bird bird = BirdFactory.GetPigeon(); 
    if (bird.GetType().Equals(typeof(Duck))) 
    { 
     Console.WriteLine("quack"); 
    } 
    else if (bird.GetType().Equals(typeof(Pigeon))) 
    { 
     Console.WriteLine("coo coo"); 
    } 
} 

相反,尝试:

interface Bird 
{ 
    void Speak(); 
} 

class Duck : Bird 
{ 
    void Speak() 
    { 
     Console.Write("quack"); 
    } 
} 

class Pigeon : Bird 
{ 
    void Speak() 
    { 
     Console.Write("coo coo"); 
    } 
} 

void Main(string[] args) 
{ 
    Bird bird = BirdFactory.GetPigeon(); 
    bird.Speak(); 
} 
+0

+1表示良好的变量名称。它比foos和酒吧更容易理解。 – 2009-09-05 04:10:01

可以移动的地方,你正在检测类之外的对象类型班上?这种方式的功能(显然取决于具体的类)实际上是与相应的类相关联的吗?

如果你检查和/或在运行时切换类型,你可能要考虑使用运行时类型Informati如果它可用于您的编译器。它确实会增加一些开销,但它可以在不创建或维护自定义方法的情况下完成您要做的工作。

与纯粹主义者不同,我并不是真的“把功能放在课堂上,所以你可以使用多态性来解决类型切换问题”。尽管这两种方法都是有效的方法(虽然“在课堂上”的方法并不总是可行的,并且/或者在概念上是干净的)。

+0

切换类型的原因在于,它会很快增加代码的复杂性并快速降低可维护性。当你开始打开类型时,你最终将开关放在你的代码中,如果有什么改变,现在你有很多地方可以改变 – IAmCodeMonkey 2009-01-03 01:36:59

通过与其他答案的协议,我觉得至少if/else块中的一些代码需要作为新的虚拟函数在具体类中移动。这将允许您利用多态性而不是使用自制反射模式来切换类型。

...另一个虚拟方法里面执行:

if (abc.getType() == 1) { 
    ... // A 
} else if (abc.getType() == 2) { 
    ... // B 
} 

认沽A和B是这样的:

class ABC { 
virtual int foo() = 0; 
virtual void doIt() = 0; // choose a proper name 
}; 

class Concrete1 : public ABC { 
    int foo() { 
    ... } 
    void doIt() { 
    ... // A 
    } 
}; 

class Concrete2 : public ABC { 
    int foo() { 
    ... } 
    void doIt() { 
    ... // B 
    } 
}; 

,改变你如果到

abc.doIt(); 

由于另一个人说,这正是动态调度的要点!除了更简洁之外,它也永远不会“忘记”处理类型。做你的开关,你可以默默无闻地处理某种特定的类型,因为当你引入一个新的实现时,你错过了更新那个地方的代码。还请记住在ABC中有一个虚拟析构函数。