C# 重构类以消除开关情况
假设我有一个这样的类,用于计算使用不同交通方式进行不同距离旅行的成本:C# 重构类以消除开关情况,c#,C#,假设我有一个这样的类,用于计算使用不同交通方式进行不同距离旅行的成本: public class TransportationCostCalculator { public double DistanceToDestination { get; set; } public decimal CostOfTravel(string transportMethod) { switch (transportMethod) {
public class TransportationCostCalculator
{
public double DistanceToDestination { get; set; }
public decimal CostOfTravel(string transportMethod)
{
switch (transportMethod)
{
case "Bicycle":
return (decimal)(DistanceToDestination * 1);
case "Bus":
return (decimal)(DistanceToDestination * 2);
case "Car":
return (decimal)(DistanceToDestination * 3);
default:
throw new ArgumentOutOfRangeException();
}
}
这很好,但开关箱对维护人员来说可能是一场噩梦,如果我以后想使用飞机或火车怎么办?那我就要换上面的课了。我可以在这里使用什么样的开关箱替代方案,以及如何使用的提示
我设想在这样的控制台应用程序中使用它,该应用程序将从命令行运行,并带有您想要使用哪种交通工具以及您想要行驶的距离的参数:
class Program
{
static void Main(string[] args)
{
if(args.Length < 2)
{
Console.WriteLine("Not enough arguments to run this program");
Console.ReadLine();
}
else
{
var transportMethod = args[0];
var distance = args[1];
var calculator = new TransportCostCalculator { DistanceToDestination = double.Parse(distance) };
var result = calculator.CostOfTravel(transportMethod);
Console.WriteLine(result);
Console.ReadLine();
}
}
}
类程序
{
静态void Main(字符串[]参数)
{
如果(参数长度<2)
{
WriteLine(“没有足够的参数来运行此程序”);
Console.ReadLine();
}
其他的
{
var transportMethod=args[0];
var距离=args[1];
var calculator=new TransportCostCalculator{DistanceToDestination=double.Parse(distance)};
var结果=计算器。旅行成本(运输法);
控制台写入线(结果);
Console.ReadLine();
}
}
}
非常感谢任何提示 对于每种类型的旅行,您都可以使用策略类。但是,您可能需要一个工厂根据传输方法创建策略,该方法可能有一个switch语句来返回相应的计算器
public class CalculatorFactory {
public static ICalculator CreateCalculator(string transportType) {
switch (transportType) {
case "car":
return new CarCalculator();
...
public class CarCalculator : ICalculator {
public decimal Calc(double distance) {
return distance * 1;
}
}
....
听起来像是依赖注入的一个很好的候选者:
interface ITransportation {
decimal CalcCosts(double distance);
}
class Bus : ITransportation {
decimal CalcCosts(double distance) { return (decimal)(distance * 2); }
}
class Bicycle : ITransportation {
decimal CalcCosts(double distance) { return (decimal)(distance * 1); }
}
class Car: ITransportation {
decimal CalcCosts(double distance) { return (decimal)(distance * 3); }
}
现在,您可以轻松创建一个新类平面:
class Plane : ITransportation {
decimal CalcCosts(double distance) { return (decimal)(distance * 4); }
}
现在为您的计算器创建一个构造函数,它需要一个ITransportation
实例。在您的CostOfTravel
-方法中,您现在可以调用ITransportation.CalcCosts(DistanceToDestination)
这样做的好处是,您可以在不更改TransportationCostCalculator
-类代码的情况下交换实际的transportation实例
要完成此设计,您还可以创建一个TransportationFactory
,如下所示:
class TransportationFactory {
ITransportation Create(string type) {
switch case "Bus": return new Bus(); break
// ...
}
你管它叫什么
ITransportation t = myFactory.Create("Bus");
TransportationCostCalculator calculator = new TransportationCostCalculator(t);
var result = myCalculator.CostOfTravel(50);
你可以这样做:
public class TransportationCostCalculator {
Dictionary<string,double> _travelModifier;
TransportationCostCalculator()
{
_travelModifier = new Dictionary<string,double> ();
_travelModifier.Add("bicycle", 1);
_travelModifier.Add("bus", 2);
_travelModifier.Add("car", 3);
}
public decimal CostOfTravel(string transportationMethod) =>
(decimal) _travelModifier[transportationMethod] * DistanceToDestination;
}
编辑:评论中提到,如果方程式需要更改而不更新代码,则不允许对其进行修改,因此我在这里写了一篇关于如何执行的帖子:。您可以这样定义一个抽象类,并让每个TransportationMethod
扩展抽象类:
abstract class TransportationMethod {
public TransportationMethod() {
// constructor logic
}
abstract public double travelCost(double distance);
}
class Bicycle : TransportationMethod {
public Bicycle() : base() { }
override public double travelCost(double distance) {
return distance * 1;
}
}
class Bus : TransportationMethod {
public Bus() : base() { }
override public double travelCost(double distance) {
return distance * 2;
}
}
class Car : TransportationMethod {
public Car() : base() { }
override public double travelCost(double distance) {
return distance * 3;
}
}
因此,在实际的方法调用中,可以这样重写:
public decimal CostOfTravel(TransportationMethod t) {
return t.travelCost(DistanceToDestination);
}
public enum TransportMethod
{
Bicycle = 1,
Bus = 2,
Car = 3
}
您可以创建一个基于传输返回乘数的
public class TransportationCostCalculator
{
Dictionary<string, int> multiplierDictionary;
TransportationCostCalculator ()
{
var multiplierDictionary= new Dictionary<string, int> ();
dictionary.Add ("Bicycle", 1);
dictionary.Add ("Bus", 2);
....
}
public decimal CostOfTravel(string transportMethod)
{
return (decimal) (multiplierDictionary[transportMethod] * DistanceToDestination);
}
公共类运输成本计算器
{
字典乘数字典;
运输成本计算器()
{
var multiperdictionary=newdictionary();
加上(“自行车”,1);
添加(“总线”,2);
....
}
公共十进制CostOfTravel(字符串传输方法)
{
返回(十进制)(乘法器字典[transportMethod]*距离目标);
}
我认为答案是某种数据库
如果您使用了一些,TransportCostCalculator会向数据库询问多人游戏的特定transportmethod
数据库可以是文本文件、xml或SQL server。只需一个键值对
如果你只想使用代码,就没有办法避免从transportmethod到multiplayer(或cost)的转换
使用数据库将字典从代码中删除,并且不能更改代码以应用新的传输方法或改变值。
< P>这是策略设计模式的一个例子。创建基类,例如“代码> ToalCuxCalpalCalpUT/CODE >,然后开发您将考虑的每种旅行模式的类,每个都覆盖一个共同点。方法,Calculate(double)
。然后可以根据需要使用factory模式实例化特定的TravelCostCalculator
诀窍在于如何构造工厂(没有switch语句)。我这样做的方法是使用静态类构造函数(publicstaticclassname()
-不是实例构造函数),在字典中向工厂注册每个策略类
因为C不确定地运行类构造函数(像大多数情况下C++一样)您必须显式运行它们以确保它们运行。这可以在主程序或工厂构造函数中完成。缺点是,如果添加策略类,还必须将其添加到要运行的构造函数列表中。您可以创建必须运行的静态方法(
Touch
或Register
)或者您也可以使用System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor
class Derived : Base
{
public static Derived()
{
Factory.Register(typeof(Derived));
}
}
// this could also be done with generics rather than Type class
class Factory
{
public static Register(Type t)
{
RegisteredTypes[t.Name] = t;
}
protected Dictionary<string, Type t> RegisteredTypes;
public static Base Instantiate(string typeName)
{
if (!RegisteredTypes.ContainsKey(typeName))
return null;
return (Base) Activator.CreateInstance(RegisteredTypes[typeName]);
}
}
派生类:基
{
公共静态派生()
{
工厂登记簿(类型(派生));
}
}
//这也可以通过泛型而不是类型类来实现
阶级工厂
{
公共静态寄存器(t型)
{
RegisteredTypes[t.Name]=t;
}
受保护的字典注册类型;
公共静态基实例化(字符串类型名)
{
如果(!RegisteredTypes.ContainsKey(typeName))
返回null;
return(Base)Activator.CreateInstance(RegisteredTypes[typeName]);
}
}
在我看来,基于您当前方法的任何解决方案都有一个关键缺陷:无论您如何分割,都是在代码中添加数据。这意味着每次您想要更改这些数字、添加新车型等,都必须编辑代码,然后重新编译、分发补丁等
你真正应该做的是把数据放在它所属的地方——放在一个单独的、未编译的文件中。你可以使用XML、JSON、某种形式的数据库,甚至只是一个简单的配置文件。如果你想加密它,不必加密
class Derived : Base
{
public static Derived()
{
Factory.Register(typeof(Derived));
}
}
// this could also be done with generics rather than Type class
class Factory
{
public static Register(Type t)
{
RegisteredTypes[t.Name] = t;
}
protected Dictionary<string, Type t> RegisteredTypes;
public static Base Instantiate(string typeName)
{
if (!RegisteredTypes.ContainsKey(typeName))
return null;
return (Base) Activator.CreateInstance(RegisteredTypes[typeName]);
}
}
public enum TransportMethod
{
Bicycle = 1,
Bus = 2,
Car = 3
}
public decimal CostOfTravel(string transportMethod)
{
var tmValue = (int)Enum.Parse(typeof(TransportMethod), transportMethod);
return DistanceToDestination * tmValue;
}