【问题标题】:What's design pattern should I use to generalize similar classes usage?我应该使用什么设计模式来概括类似的类用法?
【发布时间】:2018-04-06 14:18:15
【问题描述】:

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

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 中的设备使用更加通用和通用。

【问题讨论】:

  • 传感器中的逻辑是相同的还是不同的,我猜不同,接口似乎是一个值得的解决方案
  • 我不太确定您对这里的期望。您可以定义一个通用接口,例如IDataGetter<T>,然后使方法通用,例如T GetData(); 然后让每个班级以不同的方式修复 T,例如public class TemperatureSensor : IDataGetter<double>。但这并没有真正的帮助,因为您的每个 GetData 方法仍将返回不同的类型,因此您需要将每个返回值强制转换为该类型才能使用它。
  • jmcilhinney,请在答案文本区域中输入您的代码,cmets 不适用于答案和代码 sn-ps。我只是看到设备具有相同的方法 GetData(),我觉得存在一种优化代码的方法,使其更具可扩展性和通用性。
  • 您最终会如何处理数据(记录数据、显示给用户等)以及轮询的频率如何?
  • 这个问题中没有足够的实现细节来提供一个合理的答案。它看起来像一堆返回不同类型的属性,您希望以某种方式将所有这些数据聚合到一个地方。我猜你可以去dynamic。或者你可能需要更高级的东西。我不能只说一些看起来可以转换为属性的GetData 方法。

标签: c# .net design-patterns architecture


【解决方案1】:

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

声明一个接口

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,创建一个实例并调用 CollectData() 方法。如果需要,您可以随时调用 GetData() 而不是 CollectData()。

【讨论】:

    【解决方案2】:

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

    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 完成,那么可以使用更复杂的技术,例如处理程序或回调,将数据与数据处理分开。

    【讨论】:

      【解决方案3】:

      这取决于每个传感器的结果。您已经给出了 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);
          }
      }
      

      【讨论】:

        【解决方案4】:

        我也可能会使用 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;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-12-04
          • 2012-02-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-28
          相关资源
          最近更新 更多