C#代码重用没有太多通用参数

C#代码重用没有太多通用参数,c#,generics,code-reuse,C#,Generics,Code Reuse,我有一个叫做CommercialRiskProcessor的类。它只有221行,包括注释和使用的。它只有3个注入参数,它只做一件事——评估一些风险。实际上,它的接口只有一种方法: public interface ICommercialRiskProcessor { Task<CommercialQuoteWithStatus> ApplyRules(CommercialRiskProcessorRequestInfo info, CommercialQuoteContext

我有一个叫做
CommercialRiskProcessor
的类。它只有221行,包括注释和使用的
。它只有3个注入参数,它只做一件事——评估一些风险。实际上,它的接口只有一种方法:

public interface ICommercialRiskProcessor
{
    Task<CommercialQuoteWithStatus> ApplyRules(CommercialRiskProcessorRequestInfo info, CommercialQuoteContext qtx, EFCommercialQuote quote);
}
这只会使第一种方法
ApplyRules
看起来很好。它下面的其他(私有)方法使用更多的方法/声明类的属性,这反过来会引入更多的泛型参数和约束

如果我复制/粘贴整个文件,然后将“商业”重命名为 “屋主”,那么新的类将开箱即用,因为所有 在这些函数中,支持类和属性的调用是相同的 两组班级。然而,房主和商业阶层都是如此 非常不同,因此所有的底层对象都是不同的

听起来,即使这些类非常不同,您也可以从它们中提取相同的接口。您只需要将接口限制为风险处理所需的方法。然后,风险处理器可以对实现这些接口的任何类进行操作

如果我复制/粘贴整个文件,然后将“商业”重命名为 “屋主”,那么新的类将开箱即用,因为所有 在这些函数中,支持类和属性的调用是相同的 两组班级。然而,房主和商业阶层都是如此 非常不同,因此所有的底层对象都是不同的


听起来,即使这些类非常不同,您也可以从它们中提取相同的接口。您只需要将接口限制为风险处理所需的方法。然后,风险处理器可以对实现这些接口的任何类进行操作。

您可以尝试引入一些更高级别的抽象(如
IRiskProcessor
)并实现类似模式的东西。考虑到单一责任原则,声明代码应该只有一个更改的理由,也许让代码看起来是重复的,但表示两个不同的进程是有意义的。假设您对公共代码进行重构,然后处理“屋主”和“商业”的方式会随之发生变化。现在你必须回过头来再次把它分开。需要考虑的东西可能适用,也可能不适用。@NikP我想你的建议可以作为一个答案,因为我们最近不得不更改这些类中一组的签名,而不是其他类中的签名。如果它是普通的,那将是一场噩梦。所以,如果你能,请提交你的评论作为回答,我会接受的。谢谢!我很感激你的想法,不过我想我所做的只是轻轻推了你一下。如果你一直试着走一般路线,然后对签名进行更改,听起来你无论如何都会得到我指给你的地方。我只是一个有意见的人。不管怎样,我很高兴我能提供见解,不管是否有用,这对我来说已经足够了。你可以尝试引入一些更高级别的抽象(如
IRiskProcessor
)并实现类似于模式的东西。考虑到单一责任原则,声明代码应该只有一个更改的理由,也许让代码看起来是重复的,但表示两个不同的进程是有意义的。假设您对公共代码进行重构,然后处理“屋主”和“商业”的方式会随之发生变化。现在你必须回过头来再次把它分开。需要考虑的东西可能适用,也可能不适用。@NikP我想你的建议可以作为一个答案,因为我们最近不得不更改这些类中一组的签名,而不是其他类中的签名。如果它是普通的,那将是一场噩梦。所以,如果你能,请提交你的评论作为回答,我会接受的。谢谢!我很感激你的想法,不过我想我所做的只是轻轻推了你一下。如果你一直试着走一般路线,然后对签名进行更改,听起来你无论如何都会得到我指给你的地方。我只是一个有意见的人。不管是哪种方式,我很高兴我能提供见解,不管是否有用,这对我来说已经足够了。不,我不能从它们中提取相同的接口,我只能提取带有几个参数的通用接口:例如
qtx。RuleEvaluationResults
是一个“商业”或“房主”列表每个相关风险处理者的对象,依此类推。策略模式使用相同的基本接口运行(
IBillingStrategy
)。在这里,我没有一个通用的界面来操作。我可以创建一个通用的通用接口,但它会有太多的通用参数。@KonstantinKonstantinov那么简单的类重命名是如何工作的呢?从风险处理者的角度来看,所有相关的HO/CO类都有完全相同的被调用属性(当然,不同类型的,当然这些类还有一些不同的属性/方法)。因此,我在风险处理器文件的副本中将“商业”重命名为“房屋所有者”,然后看到了神奇的结果:)不,我不能从它们中提取相同的接口,我只能提取带有几个参数的通用接口:例如,
qtx。RuleEvaluationResults
是每个相关风险处理者的“商业”或“房主”对象列表,等等。策略模式使用相同的基本接口(示例中为
IBillingStrategy
)。在这里,我没有一个通用的界面来操作。我可以创建一个通用的通用接口,但它会有太多的通用参数。@KonstantinKonstantinov那么简单的类重命名是如何工作的呢?从风险处理者的角度来看,所有相关的HO/CO类都有完全相同的被调用属性(当然,不同的属性)
public class CommercialRiskProcessor : ServiceBase, ICommercialRiskProcessor
{
    public const string UiHardDeclineMessage = WorkflowFailure.UiDefaultWorkflowFailureMessage;
    protected ICommercialRiskRuleCollectionFactory RiskRuleCollectionFactory { get; }
    protected ICommercialRiskRuleFactory RiskRuleFactory { get; }
    protected ICommercialOverrideService OverrideService { get; }

    public CommercialRiskProcessor(
        ICommercialRiskRuleCollectionFactory riskRuleCollectionFactory,
        ICommercialRiskRuleFactory riskRuleFactory,
        ICommercialOverrideService overrideService)
    {
        RiskRuleCollectionFactory = riskRuleCollectionFactory;
        RiskRuleFactory = riskRuleFactory;
        OverrideService = overrideService;
    }

    public async Task<CommercialQuoteWithStatus> ApplyRules(CommercialRiskProcessorRequestInfo info, CommercialQuoteContext qtx, EFCommercialQuote quote)
    {
        var ruleCollection = await RiskRuleCollectionFactory.TryResolveService(info.RuleCollectionName, qtx, quote);

        if (ruleCollection == null)
        {
            return new CommercialWorkflowFailure(UiNotSupportedStateMessage);
        }

        var results = await ApplyRules(ruleCollection, qtx, quote);
        qtx.RuleEvaluationResults.AddRange(results);
        return await ProcessResults(info, results, qtx, quote);
    }

    public interface IQuoteContext<TRuleEvaluationResult>
    {
        List<TRuleEvaluationResult> RuleEvaluationResults { get; }
    }

    public interface IRiskProcessorRequestInfo<TRuleCollectionName>
    {
        TRuleCollectionName RuleCollectionName { get; }
    }

    public interface IRiskProcessor<TQuoteContext, TQuote, TRequest, TQuoteWithStatus>
    {
        Task<TQuoteWithStatus> ApplyRules(TRequest info, TQuoteContext qtx, TQuote quote);
    }

    public interface IRiskRuleCollectionFactory<TQuoteContext, TQuote, TRuleCollectionName, TRuleCollection>
    {
        Task<TRuleCollection> TryResolveService(TRuleCollectionName collectionName, TQuoteContext qtx, TQuote quote);
    }

    public abstract class RiskProcessor<TQuoteContext, TQuote, TQuoteWithStatus, TRequest, TRiskRuleCollectionFactory, TRiskRuleFactory, TOverrideService, TRuleCollectionName, TRuleCollection, TRuleEvaluationResult>
        : ServiceBase, IRiskProcessor<TQuoteContext, TQuote, TRequest, TQuoteWithStatus>
        where TRequest : IRiskProcessorRequestInfo<TRuleCollectionName>
        where TRiskRuleCollectionFactory : IRiskRuleCollectionFactory<TQuoteContext, TQuote, TRuleCollectionName, TRuleCollection>
        where TQuoteContext : IQuoteContext<TRuleEvaluationResult>
    {
        public const string UiHardDeclineMessage = WorkflowFailure.UiDefaultWorkflowFailureMessage;
        protected TRiskRuleCollectionFactory RiskRuleCollectionFactory { get; }
        protected TRiskRuleFactory RiskRuleFactory { get; }
        protected TOverrideService OverrideService { get; }

        public RiskProcessor(
            TRiskRuleCollectionFactory riskRuleCollectionFactory,
            TRiskRuleFactory riskRuleFactory,
            TOverrideService overrideService)
        {
            RiskRuleCollectionFactory = riskRuleCollectionFactory;
            RiskRuleFactory = riskRuleFactory;
            OverrideService = overrideService;
        }

        protected abstract TQuoteWithStatus ToNullRuleCollectionFailure(TRequest info, TQuoteContext qtx, TQuote quote);

        public async Task<TQuoteWithStatus> ApplyRules(TRequest info, TQuoteContext qtx, TQuote quote)
        {
            var ruleCollection = await RiskRuleCollectionFactory.TryResolveService(info.RuleCollectionName, qtx, quote);

            if (ruleCollection == null)
            {
                return ToNullRuleCollectionFailure(info, qtx, quote);
            }

            var results = await ApplyRules(ruleCollection, qtx, quote);
            qtx.RuleEvaluationResults.AddRange(results);
            return await ProcessResults(info, results, qtx, quote);
        }