我在.Net 4.0中有以下C#代码。它需要向IRetailBusiness进行IBusiness的类型转换。
//Type checking
if (bus is IRetailBusiness)
{
//Type casting
investmentReturns.Add(new RetailInvestmentReturn((IRetailBusiness)bus));
}
if (bus is IIntellectualRights)
{
investmentReturns.Add(new IntellectualRightsInvestmentReturn((IIntellectualRights)bus));
}
业务场景:
我正在为投资控股公司设计软件系统。该公司拥有零售业务和IntellectualRights业务。 BookShop和AudioCDShop是零售业务的例子。 EngineDesignPatent和BenzolMedicinePatent是IntellectualRights业务的例子。这两种业务类型完全不相关。
投资公司有一个名为InvestmentReturn
的概念(但每个企业对此概念完全无知)。 InvestmentReturn是从每个业务获得的利润,并使用ProfitElement
进行计算。对于每种“业务类型”(Retail,IntellectualRights),使用的ProfitElement是不同的。
问题
如何重构此类设计以避免此type casting
和type checking
?
抽象投资
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;
}
}
扩展
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();
}
}
客户端
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();
}
}
业务领域实体
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
参考
答案 0 :(得分:5)
此解决方案使用业务接口知道必须创建返回的概念,并且他们的具体实现知道具体的具体返回要创建的内容。
第1步将InvestmentReturn
拆分为两个接口;原始减去Business
属性和新的通用子类:
public abstract class InvestmentReturn
{
public double ProfitElement { get; set; }
public abstract double GetInvestmentProfit();
public double CalculateBaseProfit()
{
// ...
}
}
public abstract class InvestmentReturn<T>: InvestmentReturn where T : IBusiness
{
public T Business { get; set; }
}
第2步继承通用名称,以便您可以在不投标的情况下使用Business
:
public class RetailInvestmentReturn : InvestmentReturn<IRetailBusiness>
{
// this won't compile; see **Variation** below for resolution to this problem...
public RetailInvestmentReturn(IRetailBusiness retail)
{
Business = retail;
}
public override double GetInvestmentProfit()
{
ProfitElement = Business.GrossRevenue;
return CalculateBaseProfit();
}
}
第3步向IBusiness
添加一个返回InvestmentReturn
的方法:
public interface IBusiness
{
InvestmentReturn GetReturn();
}
第4步引入EntityBaseClass
的通用子级别,以提供上述方法的默认实现。如果您不这样做,您将必须为所有业务实施它。如果您执行这样做,则意味着您不想重复GetReturn()
实现的所有类必须从下面的类继承,这反过来意味着它们必须继承自{{ 1}}。
EntityBaseClass
步骤5 为每个子类实施该方法(如有必要)。以下是public abstract class BusinessBaseClass<T> : EntityBaseClass, IBusiness where T : InvestmentReturn, new()
{
public virtual InvestmentReturn GetReturn()
{
return new T();
}
}
:
BookShop
第6步修改public class BookShop : BusinessBaseClass<RetailInvestment>, IRetailBusiness
{
public double GrossRevenue { get; set; }
public BookShop(double grossRevenue)
{
GrossRevenue = grossRevenue;
}
// commented because not inheriting from EntityBaseClass directly
// public InvestmentReturn GetReturn()
// {
// return new RetailInvestmentReturn(this);
// }
}
以添加Main
的实例。您不必进行类型转换或类型检查,因为之前已经以类型安全的方式完成了此操作:
InvestmentReturn
如果您不希望您的具体企业知道任何关于创建 static void Main(string[] args)
{
var allMyProfitableBusiness = new List<IBusiness>();
// ...
var investmentReturns = allMyProfitableBusiness.Select(bus => bus.GetReturn()).ToList();
// ...
}
- 只知道他们必须在被问到时创建一个< - >那么您可能想要修改此模式以合并一个工厂,该工厂在给定输入的情况下创建返回(例如InvestmentReturn
实现与IBusiness
子类型之间的映射)。
<强>变异强>
以上所有工作正常,如果删除设置InvestmentReturn
属性的投资回报构造函数,则编译。这样做意味着在其他地方设置Business
。这可能不太可取。
另一种方法是在Business
内设置Business
属性。我找到了一种方法来做到这一点,但它确实开始让课程看起来很混乱。这是为了评估它是否值得。
从GetReturn
中删除非默认构造函数:
RetailInvestmentReturn
更改public class RetailInvestmentReturn : InvestmentReturn<IRetailBusiness>
{
public override double GetInvestmentProfit()
{
ProfitElement = Business.GrossRevenue;
return CalculateBaseProfit();
}
}
。这是双重演员变得混乱的地方,但至少它只限于一个地方。
BusinessBaseClass
最后改变你的业务。以下是public abstract class BusinessBaseClass<T, U> : EntityBaseClass, IBusiness
where T : InvestmentReturn<U>, new()
where U : IBusiness
{
public double GrossRevenue { get; set; }
public virtual InvestmentReturn GetReturn()
{
return new T { Business = (U)(object)this };
}
}
的示例:
BookShop
答案 1 :(得分:3)
通过访客模式访问IBusiness的具体实现。然后在投资回报域中创建一个可以访问每个业务的访问者,如下所示:
public interface IVisitor<T>
{
T Visit(IIntellectualRights bus);
T Visit(IRetailBusiness bus);
}
public interface IBusiness
{
T Accept<T>(IVisitor<T> visitor);
}
public class AudioCDShop : EntityBaseClass, IRetailBusiness
{
public void Accept(IVisitor visitor)
{
return visitor.Visit(this);
}
//do the same for each IBusiness implementor.
然后在您的投资回报域中:
public class InvestmentVisitor : IVisitor<InvestmentReturn>
{
public InvestmentReturn GetInvestment(IBusiness bus)
{
return bus.Accept(this);
}
public InvestmentReturn Visit(IIntellectualRights bus)
{
return new IntellectualRightsInvestmentReturn(bus)
}
public InvestmentReturn Visit(IRetailBusiness bus)
{
return new RetailInvestmentReturn(bus);
}
}
用法
var investmentReturn = new InvestmentVisitor().GetInvestment(bus);
完全未经测试且未经验证..但这个概念有效。 如果您只有两种不同类型的节点应该访问,那么这也可能有点过分。
答案 2 :(得分:1)
基本上,如果我理解你,你想避免类型转换。
您只需通过以下方式更改构造函数签名即可完成此任务:
public RetailInvestmentReturn(IRetailBusiness retail) {...}
public IntellectualRightsInvestmentReturn(IIntellectualRights intellectual) {...}
为:
public RetailInvestmentReturn(IBusiness retail) {...}
public IntellectualRightsInvestmentReturn(IBusiness intellectual) {...}
如果由于设计限制而不是选项,那么您可以尝试使用策略模式。那仍然需要一个类型演员,但你会摆脱可怕的“ifs”和“elses”,我相信,这是你真正的问题,对吧?为此,您需要:
在代码中,看起来像这样:
(创建,...)
Dictionary<Type,Func<IBusiness,InvestmentReturn>> dict = new Dictionary<Type, Func<IBusiness, InvestmentReturn>>
{
{typeof (BookShop), business => new RetailInvestmentReturn((IRetailBusiness) business)},
{typeof (AudioCDShop), business => new IntellectualRightsInvestmentReturn((IIntellectualRights) business)}
};
(使用它!)
//试试这个:
foreach (IBusiness bus in allMyProfitableBusiness)
{
investmentReturns.Add(dict[bus.GetType()](bus));
}
答案 3 :(得分:0)
让IBusiness接口处理InvestmentReturn类型并返回利润金额。
对foreach循环的更改:
foreach (IBusiness bus in allMyProfitableBusiness)
{
// No type checking or casting. It is scalable to new business types.
investmentReturns.Add(bus.GetReturnInvestment());
}
接口更新:
public interface IBusiness
{
IReturnInvestment GetReturnInvestment();
double GetProfit();
}
public abstract class EntityBaseClass
{
}
public interface IRetailBusiness : IBusiness
{
...
}
public interface IIntellectualRights : IBusiness
{
...
}
包含其他基类的业务类:
#region Intellectuals
public abstract IntellectualRightsBaseClass : EntityBaseClass, IIntellectualRights
{
...
public IReturnInvestment GetReturnInvestment()
{
return new IntellectualRightsInvestmentReturn(this);
}
public double GetProfit()
{
return this.Royalty;
}
}
public class EngineDesignPatent : IntellectualRightsBaseClass
{
...
}
public class BenzolMedicinePatent : IntellectualRightsBaseClass
{
...
}
#endregion
#region Retails
public abstract RetailBusinessBaseClass : IRetailBusiness
{
...
public IReturnInvestment GetReturnInvestment()
{
return new RetailInvestmentReturn(this);
}
public double GetProfit()
{
return this.GrossRevenue;
}
}
public class BookShop : RetailBusinessBaseClass
{
...
}
public class AudioCDShop : RetailBusinessBaseClass
{
...
}
#endregion
扩展
public class RetailInvestmentReturn : InvestmentReturn
{
public RetailInvestmentReturn(IRetailBusiness retail)
: base(retail)
{
}
/* Don't need anymore
public override double GetInvestmentProfit()
{
//GrossRevenue is the ProfitElement for RetailBusiness
ProfitElement = Business.GetProfit();
return base.CalculateBaseProfit();
} */
}
public class IntellectualRightsInvestmentReturn : InvestmentReturn
{
public IntellectualRightsInvestmentReturn(IIntellectualRights intellectual)
: base(intellectual)
{
}
/* Don't need anymore
public override double GetInvestmentProfit()
{
//Royalty is the ProfitElement for IntellectualRights Business
ProfitElement = Business.GetProfit();
return base.CalculateBaseProfit();
} */
}
抽象投资返回
public abstract class InvestmentReturn
{
protected InvestmentReturn(IBusiness business)
{
this.Business = business;
}
public double ProfitElement { get; set; }
public IBusiness Business{ get; set; }
public override double GetInvestmentProfit()
{
ProfitElement = this.Business.GetProfit();
return this.CalculateBaseProfit();
}
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;
}
}
答案 4 :(得分:0)
如何使InvestmentReturn成为具体的类并将ProfitElement逻辑转移到IBusiness?
public class InvestmentReturn
{
public double ProfitElement { get; set; }
public double InvestmentProfit{ get; set; }
public RetailInvestmentReturn(IRetailBusiness bus)
{
ProfitElement = bus.GetProfitElement();
}
public double CalculateBaseProfit()
{
......
}
}
public class BenzolMedicinePatent : EntityBaseClass, IIntellectualRights
{
public double Royalty { get; set; }
public BenzolMedicinePatent(double royalty)
{
Royalty = royalty;
}
public override double GetProfitElement()
{
return royalty;
}
}
public class BookShop : EntityBaseClass, IRetailBusiness
{
public double GrossRevenue { get; set; }
public BookShop(double grossRevenue)
{
GrossRevenue = grossRevenue;
}
public override double GetProfitElement()
{
return royalty;
}
}
现在客户端变为:
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)
{
investmentReturns.Add(new InvestmentReturn(bus));
}
double totalProfit = 0;
foreach (var profitelement in investmentReturns)
{
totalProfit = totalProfit + profitelement.GetInvestmentProfit();
Console.WriteLine("Profit: {0:c}", profitelement.GetInvestmentProfit());
}
Console.ReadKey();
}
}
答案 5 :(得分:0)
上面的所有答案似乎都比必要的更复杂,但你并不能确切地说出你需要保留哪些信息,所以很难知道你可以重新设计你的信息。类。如果你想做的只是产生上述程序产生的结果并仍然使用对象,你可以这样做:
class Program2
{
static void Main(string[] args)
{
List<Business> allMyProfitableBusiness = new List<Business>();
Business bookShop1 = new Business(75);
Business cd1Shop = new Business(80);
Business enginePatent = new Business(1200);
Business medicinePatent = new Business(1450);
allMyProfitableBusiness.Add(bookShop1);
allMyProfitableBusiness.Add(cd1Shop);
allMyProfitableBusiness.Add(enginePatent);
allMyProfitableBusiness.Add(medicinePatent);
foreach (var business in allMyProfitableBusiness)
{
Console.WriteLine("Profit: {0:c}", business.GetInvestmentProfit());
}
Console.ReadKey();
}
}
public class Business
{
private double ProfitElement;
public Business(double profitElement)
{
ProfitElement = profitElement;
}
public double GetInvestmentProfit()
{
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;
}
}
如果由于某种原因,您确实需要保留不同类型的业务(零售和知识产权),您仍然可以使用基类大大简化它。您不需要泛型或InvestmentReturn
或EntityBaseClass
(虽然您计算totalProfit
,但不会在任何地方使用):
class Program
{
static void Main(string[] args)
{
List<Business> allMyProfitableBusiness = new List<Business>();
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);
double totalProfit = 0;
foreach (var business in allMyProfitableBusiness)
{
totalProfit = totalProfit + business.GetInvestmentProfit();
Console.WriteLine("Profit: {0:c}", business.GetInvestmentProfit());
}
Console.ReadKey();
}
}
public abstract class Business
{
public abstract double Profit { get; }
public double GetInvestmentProfit()
{
double profit = 0;
if (Profit < 5)
{
profit = Profit * 5 / 100;
}
else if (Profit < 20)
{
profit = Profit * 7 / 100;
}
else
{
profit = Profit * 10 / 100;
}
return profit;
}
}
public abstract class RetailBusiness : Business
{
public double GrossRevenue { get; set; }
public override double Profit {get {return GrossRevenue; } }
}
public abstract class IntellectualRights : Business
{
public double Royalty { get; set; }
public override double Profit {get {return Royalty; } }
}
#region Intellectuals
public class EngineDesignPatent : IntellectualRights
{
public EngineDesignPatent(double royalty)
{
Royalty = royalty;
}
}
public class BenzolMedicinePatent : IntellectualRights
{
public BenzolMedicinePatent(double royalty)
{
Royalty = royalty;
}
}
#endregion
#region Retails
public class BookShop : RetailBusiness
{
public BookShop(double grossRevenue)
{
GrossRevenue = grossRevenue;
}
}
public class AudioCDShop : RetailBusiness
{
public AudioCDShop(double grossRevenue)
{
GrossRevenue = grossRevenue;
}
}
#endregion
当然,任何人读这篇文章的机会非常非常小: - )