Ped*_*ini 4 c# polymorphism dependencies
我正在研究一个模型,可以用一堆不同的车辆做一些事情。每个 Vehicle 都应该做一些事情,但每种 Vehicle 类型做不同的事情。所以我使用 .NET Framework 以这种方式实现了它:
abstract class Vehicle
{
abstract void DoStuff()
}
class Car : Vehicle
{
override void DoStuff()
{
//Do some Car stuff here
}
}
class Motorcycle : Vehicle
{
override void DoStuff()
{
//Do some Motorcycle stuff here
}
}
class Model
{
RunModel(Vehicle[] vehicleCollection)
{
foreach(Vehicle currentVehicle in vehicleCollection)
{
currentVehicle.DoStuff()
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的程序的核心功能,它按预期工作。现在我应该根据每辆车所做的事情输出报告。每种类型的车辆都应该输出不同类型的报告,所以我为它做了一个类似的解决方案:
abstract class Vehicle
{
abstract void DoStuff();
abstract Report GetReport();
}
class Car : Vehicle
{
override Report GetReport()
{
return new CarReport(this);
}
}
class Motorcycle : Vehicle
{
override Report GetReport()
{
return new MotorcycleReport(this);
}
}
abstract class Report
{
int Foo {get; set;}
Report (Vehicle _vehicle)
{
Foo = _vehicle.CommonProperty;
}
}
class CarReport : Report
{
string Bar {get; set;}
CarReport(Car _car) : base(_car)
{
Bar = _car.CarPropoerty;
}
}
class MotorcycleReport : Report
{
bool Baz {get; set;}
MotorcycleReport(Motorcycle _cycle) : base(_cycle)
{
Baz= _cycle.MotorcyclePropoerty;
}
}
class Model
{
RunModel(Vehicle[] vehicleCollection)
{
foreach(Vehicle currentVehicle in vehicleCollection)
{
currentVehicle.DoStuff()
currentVehicle.GetReport()
}
}
}
Run Code Online (Sandbox Code Playgroud)
这也工作正常,但问题是 Car 和 Motorcycle 现在依赖于 CarReport 和 MotorcycleReport。由于这是我的程序的非核心功能,并且报告结构在未来版本中可能会发生很大变化,因此我希望以报告依赖于车辆但车辆不依赖于报告的方式来实现它。
我尝试了一个外部重载方法,它获取车辆并输出正确的报告或将抽象报告(或接口 IReport)传递给车辆“GetReport”方法但是因为我的 RunModel 方法不知道它正在处理什么类型的车辆,我找不到将其映射到正确报告类型的方法。
有没有办法避免这种双向依赖?
保持核心域尽可能简单是正确的。它应该只需要处理自己的复杂性,尽可能少地受到外界的干扰和依赖。
首先想到的是,即使继承可能对Vehicle层次结构有意义。问题是,这些报告有意义吗?你会单独使用抽象基Report类吗?仅具有共同属性的一种。
您可以使用经理来接管创建Reports的责任。
public class ReportManager
{
public Report GetReport<T>(T vehicle) where T : Vehicle
{
switch (vehicle)
{
case Car car:
return new CarReport(car);
case Motorcycle motorcycle:
return new MotorcycleReport(motorcycle);
default:
throw new NotImplementedException(vehicle.ToString());
}
}
}
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它。
public class Model
{
private readonly ReportManager _reportManager;
public Model(ReportManager reportManager)
{
_reportManager = reportManager;
}
public List<Report> RunModel(Vehicle[] vehicles)
{
var reports = new List<Report>();
foreach (var vehicle in vehicles)
{
vehicle.DoStuff();
reports.Add(_reportManager.GetReport(vehicle));
}
return reports;
}
}
Run Code Online (Sandbox Code Playgroud)
您可以将工作划分为两个单独的流程。
public class Model
{
public List<CarReport> CarReports { get; private set; }
public List<MotorcycleReport> MotorcycleReports { get; private set; }
public void RunModel(Vehicle[] vehicles)
{
// 1. Do stuff
foreach (var vehicle in vehicles)
{
vehicle.DoStuff();
}
// 2. Get reports
CarReports = vehicles.OfType<Car>().Select(car => new CarReport(car)).ToList();
MotorcycleReports = vehicles.OfType<Motorcycle>().Select(motorcycle => new MotorcycleReport(motorcycle)).ToList();
}
}
Run Code Online (Sandbox Code Playgroud)
第一个方法返回一个基类列表。第二种方法在对象上存储不同类型的列表。一旦你有不同的类型,你不能再在没有首先向上转换的情况下在类型化集合中返回它们。
报告结构在未来版本中可能会有很大变化
您可以ReportType在Vehicle. 想象一下未来需要为肌肉车和家用车创建不同的报告。与其深入研究继承,您还可以仅根据枚举值生成不同的报告。