How to achieve this functionality using Generics?

不羁岁月 提交于 2019-12-06 03:57:39

To answer your questions

(1) Try making InvestmentReturnElement contravariant (and all inheritors):

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

(2) I might be wrong, but I don't think it is possible to magically create InvestmentReturnElement<T> parameterised for the calling type, when you don't know T statically when you are calling it.

Option 1: Either you can make the business class calculate its profit

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

Option 2: Or you might conclude that the businesses are so different from profit calculation perspective that you will keep the list of one type separate from the other type and handle them without generics.

Although I couldn't think of a solution that uses generics, I believe that this is what you want:

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;
    }
}

Client

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();

That's as DRY as it gets, considering that the formula is tied to the target interface.

LCJ

Following is a solution based on the first response from @Kit in Refactoring Code to avoid Type Casting

It avoids any type check with the help of generics.

Calculator Abstraction

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; }
}

Concrete Calculators

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();
    }
}

Business Abstractions

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; }
}

Concrete Businesses

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);
    }

}

Client

    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();
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!