【问题标题】:Manipulator Factory机械手厂
【发布时间】:2014-11-23 20:00:57
【问题描述】:

EDIT4 为 Truck 提供了一个属性,以显示 SetNrOfWheels 方法是为特定的 Truck 量身定制的

EDIT3 在 B - Vehicle 和 C1 - Truck 之间建立了连接

EDIT2 移除了 B 上的界面

EDIT1 将汽车改为车辆

关于我多次遇到的问题,我有两个问题。我想知道我是否必须使用另一种模式,或者是否有人可以为我指出正确/更好的方向。

如果你知道这里有一篇描述答案的文章,请告诉我,我会尝试删除这篇文章。

考虑一个抽象基类B(即Vehicle),它被多个子类C1、C2等(即Truck、Motor)继承。假设这些类是纯数据载体,即数据契约,并且包含的一些属性。尽管对于故事来说不是绝对必要的,但它们使用了一个没有参数的默认构造函数,并且所有属性都是公共的,并且是简单的值类型对象。它们可能继承也可能不继承一些预定义的属性,但最重要的是,B 或多或少是一个标记,而不是强制执行功能(因为它是一个数据契约)。

例子:

abstract Vehicle {
  int NrOfWheels { get; set;}
}

Truck : Vehicle {
  bool HasExtraTrailer { get; set; }
}

现在考虑一个 B 的操纵器,称为 BM(即 VehicleWheelSetter),它要么是抽象基类,要么是一个接口(你决定什么是最好的;))。它具有(至少是抽象的)操作 B 类的功能。假设每个机械手,即 C1M, C2M, (TruckWheelSetter, MotorWheelSetter) 都继承自 BM。重要的是,类型是强制的,因此,C1M 仅操作 C1 类型的类。这可以通过通用的 BM 来强制执行,比如 BM where T : B,尽管这是有限的(这仍然允许 XM : BM

例子:

interface IVehicleWheelSetter<in T> where T : Vehicle {
  void SetNrOfWheels(T vehicle)
}

TruckWheelSetter : IVehicleWheelSetter<Truck> {
  void SetNrOfWheels(Truck vehicle) { 
    vehicle.NrOfWheels = 2 * 3;
    if (vehicle.HasExtraTrailer) vehicle.NrOfWheels += 2 * 2;
} } }

问题 1:有没有办法确保一个具体的 BM(即 C1M)总是操纵一个具体的 B(在这种情况下是 C1)?还是需要其他模式?

假设一般来说,这比分配一个数字要复杂得多(显然可以从配置或数据库中检索)。我写 2 * 3 并不是巧合,以表明这可能是一个复杂的操作。

假设我知道我总是在一些必须进行操作的服务中获得 B 类,比如工厂,像这样:

VehicleWheelSetterFactory {
  IVehicleWheelSetter<T> CreateVehicleWheelSetter<T>() where T : Vehicle {
    // code to create the right selector here
} }

现在,有多种方法可以做到这一点:

if (typeof(T) == typeof(Truck)) 
  return (IVehicleWheelSetter<T>)(new TruckWheelSetter());
throw new NotImplementedException();

或一些更高级的基于反射的选项。

这有一些问题,除了一些“感觉不对”。也会有泛型不起作用的情况,即 T 是未知的。在这种情况下,参数中的类型可能会有所帮助:

object CreateVehicleWheelSetter(Type vehicleType) { 
  if (vehicleType == typeof(Truck)) 
    return new TruckWheelSetter();
  throw new NotImplementedException();
}

但是如何将对象转换为 IVehicleWheelSetter? T还是未知数!所以,也许界面应该是这样的:

interface ICarWheelSetter
{
    void SetNrOfWheels(Vehicle vehicle);
}

但现在我遇到了最初的问题:我不能保证 Vehicle 是 TruckWheelSetter 中的卡车。这也不能这样解决:

interface IVehicleWheelSetter
{
    void SetNrOfWheels<T>(T car) where T : Vehicle;
}

问题 2:有更好的方法吗?

【问题讨论】:

  • 看看这个答案,看看它在这种情况下是否有帮助:stackoverflow.com/a/5386425/259769
  • "如果您知道这里有一篇描述答案的文章,请告诉我,我会尽量删除这篇文章。"请不要那样做。如果问题是重复的,它将被标记为重复问题并与现有答案链接。

标签: c# .net wcf generics inheritance


【解决方案1】:

不确定我是否遇到了您的问题(如果您解析具体实例,怎么可能是未知的),但无论如何,这不是一件容易的事:

public class CarWheelSetter
{
    public static void SetNrOfWheels<T>(T car) where T : Car
    {
        WheelSetterFor<T>().SetNrOfWheels(car);
    }

    private static ICarWheelSetter<T> WheelSetterFor<T>() where T : Car
    {
        if (typeof(T) == typeof(Truck))
        {
            return new TruckWheelSetter() as ICarWheelSetter<T>;
        }

        throw new NotImplementedException();
    }
}

【讨论】:

  • 感谢您的回复。我将汽车更改为车辆。如果你得到一个你还不知道它是什么的对象(即你从一个随机车辆的集合中得到一个车辆)你可以问像'randomVehicle.GetType()'这样的东西,但你不能轻易调用通用方法我相信,对吧?
  • 一般来说:您的 Vehicle-from-a-collection-of-random-Vehicles 是特定类的实例,无论是卡车还是汽车。所以,如果你调用一个方法 DoSomeFor(T vehicle) 你实际上调用了 DoSomeFor(Truck vehicle) 或 DoSomeFor(Car vehicle)。
【解决方案2】:

考虑一个抽象基类/接口 B

这些不是可以随意互换的东西。我希望public class abstract Car 不是接口。 interface(C# 关键字的特定含义)的目的是为不相关的类定义“相同但不同”的行为。例如,我们可能想要一个IFly 接口,这样我们就可以“飞行”虫子、鸟类和轰炸机——否则它们是不相关的类。

但是CarTruck 是相关的,因此通过继承和多态的基本机制,在处理Car 子类时不需要任意接口。

假设这些类是纯数据载体

类的基本面向对象思想是数据+它的方法Car 及其派生词的想法需要充实。这将为汽车工厂设计提供信息。

我希望许多Car 属性是它们自己的类。如果车轮组件像建议的那样复杂,那么也许应该是一个类。

B 或多或少是一个标记

Car 只是一个标记——本质上什么都没有?你怎么能一无所有?通过构建任何东西,我认为这就是这个设计的发展方向。难怪您正在尝试构建像ICarWheelSetter 这样的粒度接口。但这些都是错误的。想象一下,当您对Car 及其派生类中的每个属性执行此操作时,代码会是什么样子。在我看来,这是定义不充分的域模型的症状。如图所示,我不知道 Car 是什么以及它的派生词更是如此。因为我们没有具体的工作,任何子类都可能是任何东西,代码似乎支持这个模糊的想法。

有没有办法确保一个具体的 BM(即 C1M)总是操纵一个具体的 B(在这种情况下是 C1)?还是需要其他模式?

我想起了visitor design pattern。我想象一个Garage 修复一个CarGarage 知道如何操作 Car 来修复它。此外,派生的 BMWGarage 将知道如何修复 BMW Car 子类。

所以我想象一个Garage 继承层次结构,它反映了Car 层次结构。也许不准确:ForeignCarGarage 可能会处理几种不同的Car 类型。

有没有办法确保一个具体的 BM 总是在操纵一个具体的 B?

当然。打电话给工厂,告诉它制造一辆汽车,然后为那辆汽车建造一个车库

var myBeamer = CarFactory.Create(carEnum.BMW); 
var beamerGarage = GarageFactory.Create (typeof(myBeamer).FullName);

以上建议需要abstract factory。请注意,工厂有 3 个“级别”。使用哪个取决于需要构建的复杂性。此外,我们可能会看到 builder pattern 来协调汽车(或车库)创建中的复杂步骤。

所以这可能意味着我们为特定的Car 衍生产品及其车库创建了一个工厂。这个 BMWFactory 使用 BMW 零件 - 像 BMWWheelAssembly 这样的类。

var BMWFactory = new CarFactory(carEnum.Bmw);
var myBeamer = BMWFactory.Create();

现在 CarFactory 可能会实现 interface,或者是 abstract 类 - 但我认为不是两者兼而有之。如果有基本实现,我希望有一个 abstract 类。 abstract 类可以 template 组装过程,因此组装 - 调用方法 - 以正确的顺序完成。

【讨论】:

  • 感谢您的回复。我倾向于同意数据合同的基类事物,但是,总的来说,您似乎试图反驳这些假设。在我的情况下,这些假设不容易改变,但实际上我怀疑更多的人遇到了这个问题。我很想在数据合约上编写功能,但在大多数情况下,你不能这样做。
  • 感谢您的回复。我倾向于同意数据合同的基类事物,但是,总的来说,您似乎试图反驳这些假设。尽量坚持假设,因为它们不容易改变。我很想在数据合约上编写功能,但在大多数情况下,你不能这样做。想想 WCF 合同或数据库实体。还有其他原因将数据和功能分开(功能太重,专有等)。现在,您可能会争辩说您需要包装 Truck,并为其提供功能,但最终您遇到了我的问题。
猜你喜欢
  • 1970-01-01
  • 2016-03-23
  • 2011-05-18
  • 1970-01-01
  • 2022-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多