简体   繁体   English

在抽象基础 class 的帮助下避免 DRY 违规。 如何重构它? 寻找替代品 / arguments

[英]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:这种方式有两个问题:

  1. The obvious one is that this cannot compile because, if i understood contravariants correctly it expects the exact type.显而易见的是,这无法编译,因为如果我正确理解了逆变器,它就需要确切的类型。
  2. The second problem is I have just created three distinct generic instances of the abstract class which cannot be used as common reference for the concret implementation.第二个问题是我刚刚创建了抽象 class 的三个不同的通用实例,它们不能用作具体实现的通用参考。

Questions问题

  • Is there a way to solve the problem in someway via generics and covariants without introducing a new Interface?有没有办法在不引入新接口的情况下通过 generics 和协变量以某种方式解决问题?
    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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM