I have following C# code in .Net 4.0. It requires a type casting of IBusiness to IRetailBusiness.
//Type checking
if (bus is IRetailBusiness)
{
//Type casting
investmentReturns.Add(new RetailInvestmentReturn((IRetailBusiness)bus));
}
if (bus is IIntellectualRights)
{
investmentReturns.Add(new IntellectualRightsInvestmentReturn((IIntellectualRights)bus));
}
Business Scenario:
I am designing a software system for and Investment Holding Company. The company has Retail business and IntellectualRights business. BookShop and AudioCDShop are examples of Retail business. EngineDesignPatent and BenzolMedicinePatent are examples of IntellectualRights business. These two business types are totally unrelated.
The investment company has a concept called InvestmentReturn
(But each individual business is totally ignorant about this concept). InvestmentReturn is the profit gained from each business and it is calulated using ProfitElement
. For each “Business Type” (Retail, IntellectualRights ), the ProfitElement used is different.
QUESTION
How to refactor this class design to avoid this type casting
and type checking
?
Abstract Investment
public abstract class InvestmentReturn
{
public double ProfitElement { get; set; }
public IBusiness Business{ get; set; }
public abstract double GetInvestmentProfit();
public double CalculateBaseProfit()
{
double profit = 0;
if (ProfitElement < 5)
{
profit = ProfitElement * 5 / 100;
}
else if (ProfitElement < 20)
{
profit = ProfitElement * 7 / 100;
}
else
{
profit = ProfitElement * 10 / 100;
}
return profit;
}
}
Extensions
public class RetailInvestmentReturn : InvestmentReturn
{
public RetailInvestmentReturn(IRetailBusiness retail)
{
Business = retail;
}
public override double GetInvestmentProfit()
{
//GrossRevenue is the ProfitElement for RetailBusiness
ProfitElement = ((IRetailBusiness)Business).GrossRevenue;
return base.CalculateBaseProfit();
}
}
public class IntellectualRightsInvestmentReturn : InvestmentReturn
{
public IntellectualRightsInvestmentReturn(IIntellectualRights intellectual)
{
Business = intellectual;
}
public override double GetInvestmentProfit()
{
//Royalty is the ProfitElement for IntellectualRights Business
ProfitElement = ((IIntellectualRights)Business).Royalty;
return base.CalculateBaseProfit();
}
}
Client
class Program
{
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
List<InvestmentReturn> investmentReturns = new List<InvestmentReturn>();
foreach (IBusiness bus in allMyProfitableBusiness)
{
//Type checking
if (bus is IRetailBusiness)
{
//Type casting
investmentReturns.Add(new RetailInvestmentReturn((IRetailBusiness)bus));
}
if (bus is IIntellectualRights)
{
investmentReturns.Add(new IntellectualRightsInvestmentReturn((IIntellectualRights)bus));
}
}
double totalProfit = 0;
foreach (var profitelement in investmentReturns)
{
totalProfit = totalProfit + profitelement.GetInvestmentProfit();
Console.WriteLine("Profit: {0:c}", profitelement.GetInvestmentProfit());
}
Console.ReadKey();
}
}
Business Domain Entities
public interface IBusiness
{
}
public abstract class EntityBaseClass
{
}
public interface IRetailBusiness : IBusiness
{
double GrossRevenue { get; set; }
}
public interface IIntellectualRights : IBusiness
{
double Royalty { get; set; }
}
#region Intellectuals
public class EngineDesignPatent : EntityBaseClass, IIntellectualRights
{
public double Royalty { get; set; }
public EngineDesignPatent(double royalty)
{
Royalty = royalty;
}
}
public class BenzolMedicinePatent : EntityBaseClass, IIntellectualRights
{
public double Royalty { get; set; }
public BenzolMedicinePatent(double royalty)
{
Royalty = royalty;
}
}
#endregion
#region Retails
public class BookShop : EntityBaseClass, IRetailBusiness
{
public double GrossRevenue { get; set; }
public BookShop(double grossRevenue)
{
GrossRevenue = grossRevenue;
}
}
public class AudioCDShop : EntityBaseClass, IRetailBusiness
{
public double GrossRevenue { get; set; }
public AudioCDShop(double grossRevenue)
{
GrossRevenue = grossRevenue;
}
}
#endregion
REFERENCES