我知道这很多东西但是请耐心等待我,我不知道如何全面地描述这个问题。想象一下,有一个mashine有2个设备A
& B
。每个设备都有2个可以打开和关闭的轴。 (轴实际上有更多的属性要设置,但为了简单起见,我们只关注1):
mashine
|
------------
| |
A B
------ ------
| | | |
X Y X Y
--- --- --- ---
||| ||| ||| |||
ESP ESP ESP ESP
E: Enable(bool) | S: Start(bool) | P: Position(double)
mashine由以下类表示(我无法更改!):
// MASHINE
public static class Mashine
{
public static bool Enable_B_X { get; set; }
public static bool Enable_B_Y { get; set; }
public static bool Enable_A_X { get; set; }
public static bool Enable_A_Y { get; set; }
// actually much more properties for each axis and device
}
我的目标是编写一个控件类,它将提供可用于设置这些变量的方法。我尝试使用策略模式(或至少类似于此)。 mashine部分树的模式使我尝试将模式应用两次(因为它有2个分割级别)。一旦进入可互换装置,一个进入可互换轴。这是我到目前为止的代码:
//设备控制接口和2个不同的设备类A和B
public class DeviceControl
{
public virtual IAxis Axis { get; set; }
public void Enable()
{
Axis.Enable = true;
}
public void Disable()
{
Axis.Enable = false;
}
}
public class DeviceControl_A : DeviceControl
{
public override IAxis Axis
{
get { return base.Axis as IAxis_A; }
set { base.Axis = value as IAxis_A; }
}
}
public class DeviceControl_B : DeviceControl
{
public override IAxis Axis
{
get { return base.Axis as IAxis_B; }
set { base.Axis = value as IAxis_B; }
}
}
轴表示(接口和显式类):基本上它们用于将static
类Mashine
中的变量映射到不同轴的属性
public interface IAxis
{
bool Enable { get; set; }
}
// These Interfaces are to ensure that Axis_A goes only into Device A
// and Axis_B only with device B
public interface IAxis_A : IAxis { }
public interface IAxis_B : IAxis { }
public class X_Axis_A : IAxis_A
{
public bool Enable
{
get => Mashine.Enable_A_X;
set => Mashine.Enable_A_X = value;
}
}
public class Y_Axis_A : IAxis_A
{
public bool Enable
{
get => Mashine.Enable_A_Y;
set => Mashine.Enable_A_Y = value;
}
}
public class X_Axis_B : IAxis_B
{
public bool Enable
{
get => Mashine.Enable_B_X;
set => Mashine.Enable_B_X = value;
}
}
public class Y_Axis_B : IAxis_B
{
public bool Enable
{
get => Mashine.Enable_B_Y;
set => Mashine.Enable_B_Y = value;
}
}
这是Control Class,它提供了依赖于设备和相应轴来控制mashine的方法:
public enum Device { A, B }
public enum Axis { X, Y }
public class Control
{
public DeviceControl devControl;
public void Disable(Device dev, Axis dim)
{
// initialize
InitAxisAndDevice(dev, dim);
devControl.Disable();
}
public void Enable(Device dev, Axis dim)
{
InitAxisAndDevice(dev, dim);
devControl.Enable();
}
private void InitAxisAndDevice(Device dev, Axis dim)
{
switch (dev)
{
case Device.A:
this.devControl = new DeviceControl_A();
switch (dim)
{
case Axis.X: this.devControl.Axis = new X_Axis_A(); break;
case Axis.Y: this.devControl.Axis = new Y_Axis_A(); break;
case Axis.Z: this.devControl.Axis = new Z_Axis_A(); break;
}
break;
case Device.B:
this.devControl = new DeviceControl_B();
switch (dim)
{
case Axis.X: this.devControl.Axis = new X_Axis_B(); break;
case Axis.Y: this.devControl.Axis = new Y_Axis_B(); break;
case Axis.Z: this.devControl.Axis = new Z_Axis_B(); break;
}
break;
}
}
}
问题1
如何在InitAxisAndDevice
方法中避免这种双开关案例?
问题2
有没有更好的方法来确保只有A型轴与设备A一起使用?
我有强烈的感觉,我在这种模式的应用中误解了一些东西。可能有一种不同的方法/模式更适合解决这种控制问题的映射吗?
任何帮助都非常适合。提前谢谢。
修改
由于我的解释太模糊,这是一个用例。 所有轴都有一组几乎相同的变量,必须设置如下:[enable(bool),position(double),start(bool)]
抽象点是拥有一个可以使用一个方法Enable
f.e的控件类。根据设备类型和轴类型启用任何轴。
我希望它变得更清晰
答案 0 :(得分:2)
请考虑以下代码。我们的想法是创建设备轴,设备和机器的抽象表示作为接口。然后使用工厂为({给定不可更改的)Mashine
创建具体设备和轴。
public static class Mashine
{
public static bool Enable_B_X { get; set; }
public static bool Enable_B_Y { get; set; }
public static bool Enable_A_X { get; set; }
public static bool Enable_A_Y { get; set; }
}
/// <summary>
/// Represents a single axis of a single device
/// </summary>
public interface IDeviceAxis
{
void Enable();
void Disable();
}
// general device that has two axis, but doesn't care about anything else
public interface IDevice
{
IDeviceAxis X { get; }
IDeviceAxis Y { get; }
}
// data model for an alternative Mashine representation
public interface IMachineModel
{
IDevice A { get; }
IDevice B { get; }
}
用法:
public enum Device { A, B }
public enum Axis { X, Y }
// your control class
public class Control
{
public IMachineModel devControl;
public Control()
{
// MachineFactory see below
devControl = MachineFactory.GetMachine();
}
public void Disable(Device dev, Axis dim)
{
GetAxis(dev, dim).Disable();
}
public void Enable(Device dev, Axis dim)
{
GetAxis(dev, dim).Enable();
}
private IDeviceAxis GetAxis(Device dev, Axis dim)
{
var device = GetDevice(dev);
switch (dim)
{
case Axis.X:
return device.X;
case Axis.Y:
return device.Y;
default:
throw new ArgumentException("Invalid Axis", "dim");
}
}
private IDevice GetDevice(Device dev)
{
switch (dev)
{
case Device.A:
return devControl.A;
case Device.B:
return devControl.B;
default:
throw new ArgumentException("Invalid Device", "dev");
}
}
}
缺少部分:接口的具体实现和获取整个机器表示的工厂:
// concrete machine factory
public static class MachineFactory
{
// factory for the whole Mashine wrapper
public static IMachineModel GetMachine()
{
return new MachineModel
{
A = GetDeviceA(),
B = GetDeviceB(),
};
}
// factory methods specify the connection from the wrapper classes to the Mashine
private static IDevice GetDeviceA()
{
return new MachineDevice(x => Mashine.Enable_A_X = x, y => Mashine.Enable_A_Y = y);
}
private static IDevice GetDeviceB()
{
return new MachineDevice(x => Mashine.Enable_B_X = x, y => Mashine.Enable_B_Y = y);
}
// concrete implementations can be used for the target Mashine
private class MachineDeviceAxis : IDeviceAxis
{
Action<bool> _setterFunction;
public MachineDeviceAxis(Action<bool> setter)
{
_setterFunction = setter;
}
public void Enable()
{
_setterFunction(true);
}
public void Disable()
{
_setterFunction(false);
}
}
private class MachineDevice : IDevice
{
public MachineDevice(Action<bool> xSetter, Action<bool> ySetter)
{
X = new MachineDeviceAxis(xSetter);
Y = new MachineDeviceAxis(ySetter);
}
public IDeviceAxis X { get; private set; }
public IDeviceAxis Y { get; private set; }
}
private class MachineModel : IMachineModel
{
public IDevice A { get; set; }
public IDevice B { get; set; }
}
}
关于机器抽象的使用,我认为使用enum Device
和enum Axis
没有真正的优势。比较以下代码:
// controllers
Control yourControl;
IMachineModel myMachineModel;
// usage
yourControl.Enable(Device.A, Axis.X);
myMachineModel.A.X.Enable();
如果你问我,在这里使用Control
的好处并没有真正改善代码。
关于其他参数的修改
也许Enable
和Disable
已经过度设计了?一些通用Set(value)
如何改为:
public interface IAxisParameter<TParameter>
{
void Set(TParameter value);
}
public interface IDeviceAxis
{
IAxisParameter<bool> Enabled { get; }
IAxisParameter<double> Position { get; }
IAxisParameter<bool> Start { get; }
}
用法:
public class Control
{
public void Disable(Device dev, Axis dim)
{
GetAxis(dev, dim).Enabled.Set(false);
}
public void Enable(Device dev, Axis dim)
{
GetAxis(dev, dim).Enabled.Set(true);
}
// rest of the code as in the original answer above
}
然后相应地更改内部机器实现:
// inside factory
// added implementation
private class MachineParameter<TParam> : IAxisParameter<TParam>
{
Action<TParam> _setterFunction;
public MachineParameter(Action<TParam> setter)
{
_setterFunction = setter;
}
public void Set(TParam value)
{
_setterFunction(value);
}
}
// changed implementation
private class MachineDeviceAxis : IDeviceAxis
{
public IAxisParameter<bool> Enabled { get; set; }
public IAxisParameter<double> Position { get; set; }
public IAxisParameter<bool> Start { get; set; }
}
// changed factory methods
private static IDeviceAxis GetDeviceAxisA_X()
{
return new MachineDeviceAxis
{
Enabled = new MachineParameter<bool>(x => Mashine.Enable_A_X = x),
Position = null, // TODO
Start = null, // TODO
};
}
private static IDeviceAxis GetDeviceAxisA_Y()
{
return new MachineDeviceAxis
{
Enabled = new MachineParameter<bool>(y => Mashine.Enable_A_Y = y),
Position = null, // TODO
Start = null, // TODO
};
}
private static IDevice GetDeviceA()
{
return new MachineDevice
{
X = GetDeviceAxisA_X(),
Y = GetDeviceAxisA_Y(),
};
}
private static IDevice GetDeviceB()
{
// TODO same thing for device B
return null;
}
概念应该清楚......最后你必须为实际机器的每个属性编写一些代码。 也许将工厂内的参数创建重新组织成@Fildor在其链接代码示例中所做的事情是个好主意。
编辑:保持工厂代码更紧凑
不是为每个参数使用单独的方法使工厂膨胀,而是重新创建参数设置器,以便在一个地方的所有设备中为一个参数类型创建设置器(例如CreateEnabledParameterSetters
):
// concrete machine factory
public static class MachineFactory
{
// factory for the whole Mashine wrapper
public static IMachineModel GetMachine()
{
var enabledSetters = CreateEnabledParameterSetters();
return new MachineModel
{
A = GetDevice(enabledSetters, 0 /*A*/),
B = GetDevice(enabledSetters, 1 /*B*/),
};
}
// factory methods specify the connection from the wrapper classes to the Mashine
private static Action<bool>[,] CreateEnabledParameterSetters()
{
return new Action<bool>[,]
{
{ x => Mashine.Enable_A_X = x, x => Mashine.Enable_A_Y = x },
{ x => Mashine.Enable_B_X = x, x => Mashine.Enable_B_Y = x },
};
}
private static IDeviceAxis GetDeviceAxis(Action<bool>[,] enabledSetters, int deviceIndex, int axisIndex)
{
return new MachineDeviceAxis
{
Enabled = new MachineParameter<bool>(enabledSetters[deviceIndex, axisIndex]),
Position = null, // TODO
Start = null, // TODO
};
}
private static IDevice GetDevice(Action<bool>[,] enabledSetters, int deviceIndex)
{
return new MachineDevice
{
X = GetDeviceAxis(enabledSetters, deviceIndex, 0 /*X*/),
Y = GetDeviceAxis(enabledSetters, deviceIndex, 1 /*Y*/),
};
}
// ...