我应该使用什么样的设计模式来概括类似的类用法?

时间:2018-04-06 02:51:19

标签: c# .net design-patterns architecture

我有一些设备类,例如以下示例:

public class MoveSensor() {
     public uint GetData() {
         // Some logic here
     }
}

public class TemperatureSensor {
     public double GetData() {
         // Some logic here
     }
}

public class MultiSensorUnit() {
     public MultiSensorData GetData() {
         // Some logic here
     }
}

public class MultiSensorData {
    public int SomeSensor1Data { get; set; }
    public byte SomeSensor2Data { get; set; }
    public double SomeSensor3Data { get; set; }
}

然而,我有一个班级定期从这些设备收集数据:

public class DataCollector() {
     public void CollectData() {
         // Here I want to collect a data from all devices
     }
}

看起来我应该使用界面:

public interface IDataRecievable {
    [This is a problem place] GetData();
}

但我不能这样做,因为GetData()从不同的设备返回不同的类型。我需要一种设计模式来使DataCollector中的设备的使用更加通用和通用。

4 个答案:

答案 0 :(得分:2)

当您的所有传感器都返回不同类型的数据时,您可以考虑将数据处理移动到每个传感器实现中。 如果你能做到这一点,我就会实现它。

声明一个接口

public interface IDataRecievable<T>
{
    T GetData();
    void CollectData();
}

具体课程:

public class MoveSensor : IDataRecievable<uint>
{
    public void CollectData()
    {
        //do collect logic here
    }

    public uint GetData()
    {
        //do get data
    }
}

public class TemperatureSensor : IDataRecievable<double>
{
    public void CollectData()
    {
        //do collect logic here
    }

    public double GetData()
    {
        //do get data
    }
}

数据收集器类

public class DataCollector
{
    public void CollectData()
    {
        var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
        .Where(type => !String.IsNullOrEmpty(type.Namespace))
        .Where(type => type.GetInterfaces().Any(x => x.IsGenericType 
                        && x.GetGenericTypeDefinition() == typeof(IDataRecievable<>)));

        foreach (var type in typesToRegister)
        {
            dynamic sensor = Activator.CreateInstance(type);
            sensor.CollectData();
        }
    }
}

我让所有类实现IDataRecievable&lt;&gt;,创建一个实例并调用CollectData()方法。如果需要,你总是可以调用GetData()而不是CollectData()。

答案 1 :(得分:1)

您可以将返回数据的功能封装到同一接口的各种实现中。例如,如果将显示数据,则每个传感器返回不同的类型,以不同的方式显示数据。例如:

public interface Sensor {
    Data GetData();
}

public interface Data {
    void Display();
}

public class IntData : Data {
    public void Display() { ... }
}

public class DoubleData : Data {
    public void Display() { ... }
}

public class MoveSensor : Sensor {
    public IntData GetData() {
        // ... return IntData ...
    }
}

public class TemperatureSensor : Sensor {
    public DoubleData GetData() {
        // ... return DoubleData ...
    }
}

然后一些客户端可以遍历每个传感器并显示数据:

List<Sensor> sensors = // ...

foreach (Sensor sensor in sensors) {
    sensor.Display();
}

这不仅限于显示数据,Data接口可以包含任何类型的功能。例如,如果数据需要存储到数据库中,您可以将一些代理传递给数据库,并且每个Data实现都知道如何将自己存储在数据库中:

public class DatabaseProxy {
    public void StoreInt(int value) { ... }
    public void StoreDouble(double value) { ... }
}

public interface Data {
    void StoreData(DatabaseProxy proxy);
}

public class IntData : Data {

    private int _value;

    public IntData(int value) {
        _value = value;
    }

    public void StoreData(DatabaseProxy proxy) {
        proxy.StoreInt(_value);
    }
}

public class DoubleData : Data {

    private double _value;

    public DoubleData(double value) {
        _value = value;
    }

    public void StoreData(DatabaseProxy proxy) {
        proxy.StoreDouble(_value);
    }
}

我们的想法是将使用返回数据的责任从一些外部实体转移出来并将其交给数据本身。因此,Data实现最接近它存储的数据,因此它应该负责处理它。如果Data需要完成太多事情,那么可以使用更复杂的技术,例如处理程序或回调来将数据与数据处理分开。

答案 2 :(得分:0)

这取决于每个传感器的结果。你给出了一个uint和一个double的例子。我想其他传感器理论上可以返回字符串甚至是复杂的复合对象。

传感器的答案毫无意义,如果不知道传感器测量的是什么,消费者显然会知道这些数据。您的问题是在DataCollector的中间存储中吗?

我可以想象你想要拥有任意数量的传感器及其结果的词典。你能忍受拳击/拆箱性能开销吗?如果没有大量的传感器,那么这应该可以忽略不计。如果是这样,您可以执行以下操作:

using System;
using System.Collections.Generic;
using System.Linq;

public interface IDataRecievable 
{
    object GetData();
}

public class PiSensor : IDataRecievable
{
     public object GetData() {
         return (object)3.14m;
     }
}

public class StringSensor : IDataRecievable
{
     public object GetData() {
         return (object)"Hello World";
     }
}


public class DataCollector
{

   private List<IDataRecievable> sensors;

   private Dictionary<Type, object> sensorResults = new Dictionary<Type, object>();

   public DataCollector(IEnumerable<IDataRecievable> sensorsToPoll)
   {
       this.sensors = sensorsToPoll.ToList();
   }

   public T GetResultFromSensor<T>(Type sensorType)
   {
      return (T)this.sensorResults[sensorType];
   }

    public void CollectData()
    {
        foreach (IDataRecievable sensor in this.sensors)
        {
            sensorResults[sensor.GetType()] = sensor.GetData();
        }           
    }    
}

public class Program
{       
    public static void Main()
    {
        List<IDataRecievable> sensors = new List<IDataRecievable>
        {
            new PiSensor(),
            new StringSensor()
        };          
        DataCollector dc = new DataCollector(sensors);    
        dc.CollectData();           
        decimal pi = dc.GetResultFromSensor<decimal>(typeof(PiSensor));
        string greeting = dc.GetResultFromSensor<string>(typeof(StringSensor));

        Console.WriteLine(2 * pi);
        Console.WriteLine(greeting);
    }
}

答案 3 :(得分:0)

我也可能会选择Trung Le的实现,但我也会有一些接口来提供收集的结果:

public interface ICollectResultReceiver
{
    void ReceiveCollectResult(
        // whatever you are storing
        object someData
    );
}

我还会在收集过程中使用另一个界面,因为感觉数据收集不是获取数据的一部分:

public interface IDataRecievable<T>
{
    T GetData();
}

public interface IDataCollectable
{
    void CollectData(ICollectDataResultReceiver resultReceiver);
}

这样IDataCollectable类的用户不需要关心类型,只需使用for循环来运行所有集合。

public interface IDataCollector
{
    void Add(IDataCollectable collectable);
    void CollectData();
}

public class DataCollector : IDataCollector
{
    private readonly ICollectDataResultReceiver _resultReceiver;
    private readonly List<IDataCollectable> _collectables = new List<IDataCollectable>();

    public DataCollector(ICollectDataResultReceiver resultReceiver)
    {
        _resultReceiver = resultReceiver;
    }

    public void Add(IDataCollectable collectable)
    {
        _collectables.Add(collectable);
    }

    public void CollectData()
    {
        foreach(var collectable in _collectables)
        {
            collectable.CollectData(_resultReceiver);
        }
    }
}

除此之外,我还会为每个传感器制作接口,因为有时候需要了解您使用的模块类型。

public interface IMoveSensorDataReceivable : IDataReceivable<uint> { }
public interface ITemperatureSensorDataReceivable : IDataReceivable<double> { }
public interface IMultiSensorDataReceivable : IDataReceivable<MultiSensorData> { }

所以MultiSensorData会更清楚它有什么样的数据。

public class MultiSensorData
{
    public uint GetSensorData1() => _moveSensor.GetData();
    public double GetSensorData2() => _temperatureSensor.GetData();

    private readonly IMoveSensorDataReceivable _moveSensor;
    private readonly ITemperatureSensorDataReceivable _temperatureSensor;

    public MultiSensorData(
        IMoveSensorDataReceivable moveSensor, 
        ITemperatureSensorDataReceivable temperatureSensor)
    {
        _moveSensor = moveSensor;
        _temperatureSensor = temperatureSensor;
    }
}

使用接口而不是具体类来进行更好的测试也很重要。即使MultiSensorData不需要太多测试,您的测试也是如此:

public class TestClass
{
    [Fact]
    public void MultiSensorDataTest()
    {
        var dummyTempSensor = new DummyTempSensor();
        var dummyMoveSensor = new DummyMoveSensor();
        var multiSensorData = new MultiSensorData(dummyMoveSensor, dummyTempSensor);

        Assert.Equal(10, multiSensorData.GetSensorData1());
        Assert.Equal(0.5, multiSensorData.GetSensorData2());
    }

    private class DummyTempSensor : ITemperatureSensorDataReceivable
    {
        public double GetData() => 0.5;
    }

    private class DummyMoveSensor : IMoveSensorDataReceivable
    {
        public uint GetData() => 10;
    }
}