c# - Refactoring Code to avoid Type Casting -
i have following c# code in .net 4.0. requires type casting of ibusiness iretailbusiness.
//type checking if (bus iretailbusiness) { //type casting investmentreturns.add(new retailinvestmentreturn((iretailbusiness)bus)); } if (bus iintellectualrights) { investmentreturns.add(new intellectualrightsinvestmentreturn((iintellectualrights)bus)); }
business scenario:
i designing software system , investment holding company. company has retail business , intellectualrights business. bookshop , audiocdshop examples of retail business. enginedesignpatent , benzolmedicinepatent examples of intellectualrights business. these 2 business types totally unrelated.
the investment company has concept called investmentreturn
(but each individual business totally ignorant concept). investmentreturn profit gained each business , calulated using profitelement
. each “business type” (retail, intellectualrights ), profitelement used different.
question
how refactor class design avoid type casting
, 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 profitelement retailbusiness profitelement = ((iretailbusiness)business).grossrevenue; return base.calculatebaseprofit(); } } public class intellectualrightsinvestmentreturn : investmentreturn { public intellectualrightsinvestmentreturn(iintellectualrights intellectual) { business = intellectual; } public override double getinvestmentprofit() { //royalty profitelement 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 iretailbusiness) { //type casting investmentreturns.add(new retailinvestmentreturn((iretailbusiness)bus)); } if (bus 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
this solution uses notions business interfaces know must create return , concrete implementations know kind of concrete return create.
step 1 split investmentreturn
2 interfaces; original minus business
property , new generic subclass:
public abstract class investmentreturn { public double profitelement { get; set; } public abstract double getinvestmentprofit(); public double calculatebaseprofit() { // ... } } public abstract class investmentreturn<t>: investmentreturn t : ibusiness { public t business { get; set; } }
step 2 inherit generic 1 can use business
without casting:
public class retailinvestmentreturn : investmentreturn<iretailbusiness> { // won't compile; see **variation** below resolution problem... public retailinvestmentreturn(iretailbusiness retail) { business = retail; } public override double getinvestmentprofit() { profitelement = business.grossrevenue; return calculatebaseprofit(); } }
step 3 add method ibusiness
returns investmentreturn
:
public interface ibusiness { investmentreturn getreturn(); }
step 4 introduce generic sublcass of entitybaseclass
provide default implementation of above method. if don't you'll have implement businesses. if do means of classes don't want repeat getreturn()
implementation must inherit class below, in turn means must inherit entitybaseclass
.
public abstract class businessbaseclass<t> : entitybaseclass, ibusiness t : investmentreturn, new() { public virtual investmentreturn getreturn() { return new t(); } }
step 5 implement method each of subclasses if necessary. below example bookshop
:
public class bookshop : businessbaseclass<retailinvestment>, iretailbusiness { public double grossrevenue { get; set; } public bookshop(double grossrevenue) { grossrevenue = grossrevenue; } // commented because not inheriting entitybaseclass directly // public investmentreturn getreturn() // { // return new retailinvestmentreturn(this); // } }
step 6 modify main
add instances of investmentreturn
. don't have typecast or type-check because that's been done earlier in type safe way:
static void main(string[] args) { var allmyprofitablebusiness = new list<ibusiness>(); // ... var investmentreturns = allmyprofitablebusiness.select(bus => bus.getreturn()).tolist(); // ... }
if don't want concrete businesses know anything creating investmentreturn
—only knowing must create 1 when asked—then you'll want modify pattern incorporate factory creates returns given input (e.g. map between ibusiness
implementations , investmentreturn
subtypes).
variation
all of above works fine , compile if remove investment return constructors set business
property. doing means setting business
elsewhere. might not desirable.
an alternative set business
property inside getreturn
. found way that, starts make classes messy. it's here evaluation whether worth it.
remove non-default constructor retailinvestmentreturn
:
public class retailinvestmentreturn : investmentreturn<iretailbusiness> { public override double getinvestmentprofit() { profitelement = business.grossrevenue; return calculatebaseprofit(); } }
change businessbaseclass
. gets messy double-cast, @ least it's limited 1 place.
public abstract class businessbaseclass<t, u> : entitybaseclass, ibusiness t : investmentreturn<u>, new() u : ibusiness { public double grossrevenue { get; set; } public virtual investmentreturn getreturn() { return new t { business = (u)(object)this }; } }
finally change businesses. here's example bookshop
:
public class bookshop : businessbaseclass<retailinvestmentreturn, iretailbusiness>, iretailbusiness { // ... }
Comments
Post a Comment