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

  1. refactor code : avoiding casting in derived class
  2. cast generic type in c#
  3. how visitor implementation can handle unknown nodes
  4. open closed principle , visitor pattern implementation in c#

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

Popular posts from this blog

php - regexp cyrillic filename not matches -

c# - OpenXML hanging while writing elements -

sql - Select Query has unexpected multiple records (MS Access) -