如何使用Generics实现此功能?

时间:2014-01-30 16:37:40

标签: c# oop generics design-patterns dry

我没有使用.Net泛型的经验。我正在为.Net 4.0中的投资控股公司设计软件系统。该公司拥有零售业务和IntellectualRights业务。 BookShop和AudioCDShop是零售业务的例子。 EngineDesignPatent和BenzolMedicinePatent是IntellectualRights业务的例子。这两种业务类型完全不相关。

投资公司有一个名为InvestmentReturn的概念。这是从每个企业获得的利润。对于每个“业务类型”(Retail,IntellectualRights),投资回报的计算逻辑是不同的。

我需要通过计算每种“业务类型”的投资来创建InvestmentReturnCalculator。

public static class InvestmentReturnCalculator
{
    public static double GetNetInvestementReturn(List<IBusiness> allMyProfitableBusiness, List<InvestmentReturnElement<IBusiness>> profitElements)
    {
        double totalReturn = 0;
        foreach (IBusiness b in allMyProfitableBusiness)
        {
            //How to do calculation?
        }
        return totalReturn;
    }
}

问题

  1. 如何在List<InvestmentReturnElement<IBusiness>>函数中的Main profitElements中添加各种业务元素?

    注意:执行以下操作时出现编译错误 profitElements.Add(salesProfitBook);

  2. 如何以通用方式实现GetNetInvestementReturn方法?如果我按如下方式编写代码,则会针对不同类型重复算法。当算法对于多种类型相同时,可以使用泛型。因此,以下方法不是DRY

  3. 注意:以下代码无法编译。

        foreach (IBusiness b in allMyProfitableBusiness)
        {
            if (b is IRetailBusiness)
            {
                RetailProfit<IRetailBusiness> retailInvestmentProfit = new RetailProfit<IRetailBusiness>();
                totalReturn = totalReturn + retailInvestmentProfit.GetInvestmentProfit(b);
            }
            else if (b is IIntellectualRights)
            {
                IntellectualRightsProfit<IIntellectualRights> intellectualRightsInvestmentProfit = new IntellectualRightsProfit<IIntellectualRights>();
                totalReturn = totalReturn + intellectualRightsInvestmentProfit.GetInvestmentProfit(b);
            }
        }
    

    更新

    BookShopEngineDesignPatent继承了不同的基类。因此,我无法将IBusinessIRetailBusinessIIntellectualRights作为抽象类。它们应保持为interfaces

    现在@Grzenio的建议是在每个实体(BookShop,AudioCDShop等)中实现GetInvestmentProfit方法。在这里,我将重复相同的代码。再次,这不满足DRY

    此外,InvestmentReturn概念适用于投资控股公司。各种业务类型都没有意识到这样的概念。

    投资回报元素

        public abstract class InvestmentReturnElement<T>
        {
            public abstract double GetInvestmentProfit(T obj);
        }
    
        public class RetailProfit<T> : InvestmentReturnElement<T> where T : IRetailBusiness
        {
            public override double GetInvestmentProfit(T item)
            {
                return item.Revenue * 5/100;
            }
        }
    
        public class IntellectualRightsProfit<T> : InvestmentReturnElement<T> where T : IIntellectualRights
        {
            public override double GetInvestmentProfit(T item)
            {
                return item.Royalty * 10/100;
            }
        }      
    

    商家类型抽象

    public interface IBusiness
    {
    
    }
    
    public interface IRetailBusiness : IBusiness
    {
        bool IsOnSale { get; set; }
        double Revenue { get; set; }
    }
    
    public interface IIntellectualRights : IBusiness
    {
        double Royalty { get; set; }
    }
    

    具体业务

        #region Intellectuals
        public class EngineDesignPatent : IIntellectualRights
        {
            public double Royalty { get; set; }
        }
    
        public class BenzolMedicinePatent : IIntellectualRights
        {
            public double Royalty { get; set; }
        }
        #endregion
    
        #region Retails
        public class BookShop : IRetailBusiness
        {
            public bool IsOnSale { get; set; }
            public double Revenue { get; set; }
        }
    
        public class AudioCDShop : IRetailBusiness
        {
            public bool IsOnSale { get; set; }
            public double Revenue { get; set; }
        }
        #endregion
    

    客户端

        class Program
        {
    
            static void Main(string[] args)
            {
    
                #region MyBusines
    
                List<IBusiness> allMyProfitableBusiness = new List<IBusiness>();
    
                BookShop bookShop1 = new BookShop();
                AudioCDShop cd1Shop = new AudioCDShop();
                EngineDesignPatent enginePatent = new EngineDesignPatent();
                BenzolMedicinePatent medicinePatent = new BenzolMedicinePatent();
    
                allMyProfitableBusiness.Add(bookShop1);
                allMyProfitableBusiness.Add(cd1Shop);
                allMyProfitableBusiness.Add(enginePatent);
                allMyProfitableBusiness.Add(medicinePatent);
    
                #endregion
    
                List<InvestmentReturnElement<IBusiness>> profitElements = new List<InvestmentReturnElement<IBusiness>>();
    
                var salesProfitBook = new RetailProfit<BookShop>();
                var salesProfitAudioCD = new RetailProfit<AudioCDShop>();
                var intellectualProfitEngineDesign = new IntellectualRightsProfit<EngineDesignPatent>();
                var intellectualProfitBenzolMedicine = new IntellectualRightsProfit<BenzolMedicinePatent>();
    
                //profitElements.Add(salesProfitBook);
                Console.ReadKey();
            }
    
        }
    

3 个答案:

答案 0 :(得分:1)

回答你的问题

(1)尝试制作InvestmentReturnElement逆变(和所有继承人):

public abstract class InvestmentReturnElement<in T>
{
    public abstract double GetInvestmentProfit(T obj);
}

(2)我可能错了,但我不认为可以神奇地创建InvestmentReturnElement<T>参与调整类型,当你不要在你打电话时静态地知道T

选项1:您可以让商务舱计算其利润

public interface IBusiness
{
    double Profit {get;}
    double OtherProfit (SomeCalculatorObject o);
}

选项2:或者您可能会认为业务与利润计算的观点如此不同,以至于您将一种类型的列表与其他类型分开,并在没有泛型的情况下处理它们。

答案 1 :(得分:1)

虽然我想不出使用泛型的解决方案,但我相信这就是你想要的:

public abstract class InvestmentReturnElement
{
    protected InvestmentReturnElement(IBusiness business)
    {
        this.Business = business;
    }

    public IBusiness Business { get; private set; }

    public abstract double GetInvestmentProfit();
}

public class RetailProfit : InvestmentReturnElement
{
    public RetailProfit(IRetailBusiness retailBusiness)
        : base(retailBusiness)
    {
    }

    public override double GetInvestmentProfit()
    {
        return ((IRetailBusiness)this.Business).Revenue * 5 / 100;
    }
}

public class IntellectualRightsProfit : InvestmentReturnElement
{
    public IntellectualRightsProfit(IIntellectualRights intellectualRightsBusiness)
        : base(intellectualRightsBusiness)
    {
    }

    public override double GetInvestmentProfit()
    {
        return ((IIntellectualRights)this.Business).Royalty * 10 / 100;
    }
}

<强>客户端

List<InvestmentReturnElement> profitElements = new List<InvestmentReturnElement>();

var salesProfitBook = new RetailProfit(bookShop1);
var salesProfitAudioCD = new RetailProfit(cd1Shop);
var intellectualProfitEngineDesign = new IntellectualRightsProfit(enginePatent);
var intellectualProfitBenzolMedicine = new IntellectualRightsProfit(medicinePatent);

profitElements.Add(salesProfitBook);
profitElements.Add(salesProfitAudioCD);
profitElements.Add(intellectualProfitEngineDesign);
profitElements.Add(intellectualProfitBenzolMedicine);

foreach (var profitelement in profitElements)
{
    Console.WriteLine("Profit: {0:c}", profitelement.GetInvestmentProfit());
}

Console.ReadKey();

考虑到公式与目标接口相关联,这就像DRY一样。

答案 2 :(得分:0)

以下是基于Refactoring Code to avoid Type Casting

中来自@Kit的第一个回复的解决方案

它可以避免在泛型的帮助下进行任何类型检查。

enter image description here

计算器抽象

public abstract class InvestmentReturnCalculator
{
    public double ProfitElement { get; set; }
    public abstract double GetInvestmentProfit();

    protected double CalculateBaseProfit()
    {
        double profit = 0;
        if (ProfitElement < 5)
        {
            profit = ProfitElement * 5 / 100;
        }
        else
        {
            profit = ProfitElement * 10 / 100;
        }
        return profit;
    }
}

public abstract class InvestmentReturnCalculator<T> : InvestmentReturnCalculator where T : IBusiness
{
    public T BusinessType { get; set; }
}

具体计算器

public class RetailInvestmentReturnCalculator : InvestmentReturnCalculator<IRetailBusiness>
{
    public RetailInvestmentReturnCalculator(IRetailBusiness retail)
    {
        BusinessType = retail;
        //Business = new BookShop(100);
    }

    public override double GetInvestmentProfit()
    {
        ProfitElement = BusinessType.GrossRevenue;
        return CalculateBaseProfit();
    }
}

public class IntellectualRightsInvestmentReturnCalculator : InvestmentReturnCalculator<IIntellectualRights>
{

    public IntellectualRightsInvestmentReturnCalculator(IIntellectualRights intellectual)
    {
        BusinessType = intellectual;
    }

    public override double GetInvestmentProfit()
    {
        ProfitElement = BusinessType.Royalty;
        return base.CalculateBaseProfit();
    }
}

业务抽象

public interface IBusiness
{
    InvestmentReturnCalculator GetMyInvestmentReturnCalculator();
}

public abstract class EntityBaseClass
{

}

public interface IRetailBusiness : IBusiness
{
    double GrossRevenue { get; set; }
}

public interface IIntellectualRights : IBusiness
{
    double Royalty { get; set; }
}

具体业务

public class EngineDesignPatent : EntityBaseClass, IIntellectualRights
{
    public double Royalty { get; set; }
    public EngineDesignPatent(double royalty)
    {
        Royalty = royalty;
    }

    public InvestmentReturnCalculator GetMyInvestmentReturnCalculator()
    {
        return new IntellectualRightsInvestmentReturnCalculator(this);
    }
}

public class BookShop : EntityBaseClass, IRetailBusiness
{
    public double GrossRevenue { get; set; }
    public BookShop(double grossRevenue)
    {
        GrossRevenue = grossRevenue;
    }

    public InvestmentReturnCalculator GetMyInvestmentReturnCalculator()
    {
        return new RetailInvestmentReturnCalculator(this);
    }
}

public class AudioCDShop : EntityBaseClass, IRetailBusiness
{
    public double GrossRevenue { get; set; }
    public AudioCDShop(double grossRevenue)
    {
        GrossRevenue = grossRevenue;
    }

    public InvestmentReturnCalculator GetMyInvestmentReturnCalculator()
    {
        return new RetailInvestmentReturnCalculator(this);
    }

}

客户

    static void Main(string[] args)
    {

        #region MyBusines

        List<IBusiness> allMyProfitableBusiness = new List<IBusiness>();

        BookShop bookShop1 = new BookShop(75);
        AudioCDShop cd1Shop = new AudioCDShop(80);
        EngineDesignPatent enginePatent = new EngineDesignPatent(1200);
        BenzolMedicinePatent medicinePatent = new BenzolMedicinePatent(1450);

        allMyProfitableBusiness.Add(bookShop1);
        allMyProfitableBusiness.Add(cd1Shop);
        allMyProfitableBusiness.Add(enginePatent);
        allMyProfitableBusiness.Add(medicinePatent);

        #endregion

        var investmentReturns = allMyProfitableBusiness.Select(bus => bus.GetMyInvestmentReturnCalculator()).ToList();

        double totalProfit = 0;
        foreach (var profitelement in investmentReturns)
        {
            totalProfit = totalProfit + profitelement.GetInvestmentProfit();
            Console.WriteLine("Profit: {0:c}", profitelement.GetInvestmentProfit());
        }

        Console.ReadKey();
    }