[英]Avoid DRY violation with help of abstract base class. How to refactor it? Looking for alternatives / arguments
(Note: one solution which works you can find at the bottom of this question) (注意:您可以在此问题的底部找到一种可行的解决方案)
I am currently trying to refactor a generic class/interface where I have a covariant and contravariant parameter.我目前正在尝试重构一个通用类/接口,其中我有一个协变和逆变参数。 The reason for the refactor is that at the moment I have to implement for all given use-cases the class N-times.重构的原因是,目前我必须为所有给定的用例实现 class N 次。
My aim / the Idea therefor were to write an abstract class containing shared functionality and make the one complex method abstract which needs a concrete implementation for each use-case.我的目标/想法是编写一个包含共享功能的抽象 class 并使一个复杂的方法抽象,每个用例都需要一个具体的实现。 The abstract base class would for legacy reasons still inherit from the original interface which is a generic one.由于遗留原因,抽象基础 class 仍将继承自通用接口的原始接口。
you can find the Original structure of the code here: https://dotnetfiddle.net/ZgGihl你可以在这里找到代码的原始结构: https://dotnetfiddle.net/ZgGihl
What i tried to do was the following code snippet我试图做的是以下代码片段
https://dotnetfiddle.net/Aub8Ov https://dotnetfiddle.net/Aub8Ov
The are two problems with this way:这种方式有两个问题:
Questions问题
interface test <out TInput, in TResult>
{
void setResult(TResult result);
TInput GetInput();
}
abstract class SemiConcreteTest<TInput, TResult> : test<TInput, TResult>
{
public TResult field;
public void setResult(TResult result)
{
field = result;
}
public abstract TInput GetInput();
}
//The UseCases
....
class concreteTest_A<TResult> : SemiConcreteTest<int,TResult>
{
public override int GetInput()
{
//Some information gathering
throw new System.NotImplementedException();
}
}
//UsesCase Specific Classes
interface Itag
{
}
class A : Itag{}
class B : Itag{}
class C : Itag{}
This way would still lead to n different reference as seen in the example.如示例中所示,这种方式仍会导致 n 个不同的引用。 The first Generic parameter could be ditched by using generics on the method directly instead on the class but this would still left the problem regarding the contravariant第一个通用参数可以通过直接在方法上使用 generics 而不是在 class 上使用来放弃,但这仍然会留下关于逆变的问题
public static void Main()
{
SemiConcreteTest<int, Itag> a = new concreteTest_A<A>();
SemiConcreteTest<short, Itag> b = new concreteTest_B<B>();
SemiConcreteTest<long, Itag> c = new concreteTest_C<C>();
}
My Solution ditching the contravarian parameter我的解决方案放弃了逆变参数
What i would do personally is to ditch the contravariant parameter alltogther and replace it with a proper interface but i was asked by coworker , if possible, to avoid that .我个人会做的是完全放弃逆变参数并用适当的接口替换它,但如果可能的话,我被同事要求避免这种情况。
My solution would have been this ( https://dotnetfiddle.net/iql682 ):我的解决方案是这样的( https://dotnetfiddle.net/iql682 ):
interface DitchTheContraVariant{}
//The legacy Interface
interface ITest
{
void setResult(DitchTheContraVariant result);
TInput GetInput<TInput>();
}
I Solve the issue regarding the shared reference by using a generic method.我通过使用通用方法解决了有关共享引用的问题。 as seen above in the interface.如上界面所示。
abstract class SemiConcreteTest : ITest {
DitchTheContraVariant result;
public void setResult(DitchTheContraVariant result){
this.result = result;
}
public abstract TInput GetInput<TInput>();
}
//The UseCases
class concreteTest_A : SemiConcreteTest
{
public override T GetInput<T>()
{
//Some information gathering
throw new System.NotImplementedException();
}
}
class A :DitchTheContraVariant {}
class B :DitchTheContraVariant {}
class C :DitchTheContraVariant {}
public static void Main()
{
SemiConcreteTest a = new concreteTest_A();
a = new concreteTest_B();
a = new concreteTest_C();
a.setResult(new A());
int b = a.GetInput<int>();
Console.WriteLine("Hello");
}
But this would introduce a new interface into the codebase which i try to avoid but i have no idea how.但这会在代码库中引入一个新接口,我试图避免但我不知道如何。 I am not sure how to sell my version to my coworker.我不知道如何将我的版本卖给我的同事。
Assuming the goal is to have share the logic of certain methods, we can do it the following way:假设目标是共享某些方法的逻辑,我们可以通过以下方式实现:
Define an abstract class for your test为您的测试定义一个抽象 class
abstract class SharedTest<TInput, TResult> : Itest<TInput, TResult>
{
TResult result;
public virtual void setResult(TResult result)
{
this.result = result;
}
public abstract TInput GetInput();
}
setResult
is marked as virtual
just in case other concrete tests want to override the base implementation. setResult
被标记为virtual
,以防其他具体测试想要覆盖基本实现。 If this isn't required, remove the virtual
keyword.如果这不是必需的,请删除virtual
关键字。
This allows your classes to be implemented like:这允许您的类实现如下:
class concreteTest_A : SharedTest<int,A>
{
public override int GetInput()
{
//Some information gathering
throw new System.NotImplementedException();
}
}
class concreteTest_B : SharedTest<short, B>
{
public override short GetInput()
{
//Some information gathering
throw new System.NotImplementedException();
}
}
class concreteTest_C : SharedTest<long, C>
{
public override long GetInput()
{
//Some information gathering
throw new System.NotImplementedException();
}
}
Usage:用法:
Itest<int, A> a = new concreteTest_A();
concreteTest_B b = new concreteTest_B();
SharedTest<long, C> c = new concreteTest_C();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.