设计多态子类,以便可以轻松添加新的子类而无需重新部署

时间:2015-04-06 21:21:02

标签: inheritance design-patterns anti-patterns extensibility ooad

我正在重新设计用于数据采集的应用程序。 我们有一个名为Sensor的基类,它或多或少如下:

public abstract class Sensor
{
    public virtual String Name { get; }
    public virtual UnitOfMeasurement Unit { get; }
    public virtual int SamplingRate { get; }
    public virtual CalibrationModel Calibration { get; }
}

每个子类的每个属性都有一个“硬编码”值。例如EmgSensor将“EMG”作为名称,将“UnitOfMeasurement.Volt”作为单位等等。

我们目前的系统遇到了这个问题我想解决的问题:每次开发新的实际传感器硬件并添加到我们的产品组合中时,我们都需要进行以下“猎枪手术”:

  1. 创建一个新的Sensor子类;
  2. 对系统的其余部分执行一些零散的微小更改(由于依赖性等);
  3. 向我们的客户群发布更新。
  4. 据我所知,每个物理传感器(硬件)都是一个概念单元,因此应该能够“插入”正在运行的系统,以便不需要重新编译。

    另一方面,如果要将Sensor的每个子类型视为一个类,我怎样才能从配置文件中创建一个“动态”类?我应该使用工厂模式吗?是否还有其他更合适的方式来实现我的需求?

    我正在使用C#(虽然我认为这是问题的偶然内容)。

1 个答案:

答案 0 :(得分:1)

.NET使动态加载类型变得非常容易。我所做的只是定义一个文件夹,我删除包含新传感器的程序集。我有一个加载器类,用于加载文件夹中的程序集,然后检查它们是否为Sensor的子类,创建子类的实例并将它们返回到列表中。

CoreLibrary程序集包含抽象基类(以及它需要的任何其他内容)

namespace CoreLibrary
{
    public abstract class Sensor
    {
        public abstract String Name { get; }
        public abstract UnitOfMeasurement Unit { get; }
        public abstract int SamplingRate { get; }
        public abstract CalibrationModel Calibration { get; }
    }
}

DynamicClassLoader是具有可以做有趣事情的加载器类的程序集。它引用了CoreLibrary ......

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Configuration;
using CoreLibrary;

namespace DynamicClassLoader
{
    public class Loader
    {
        public List<Sensor> LoadDynamicSensors()
        {
            var sensors = new List<Sensor>();
            if (!Directory.Exists(DynamicAssemblyFolder))
            {
                return sensors;
            }
            var dllFileNames = Directory.GetFiles(DynamicAssemblyFolder, "*.dll");

            foreach (var dllFileName in dllFileNames)
            {
                var dynamicAssembly = Assembly.LoadFrom(dllFileName);
                foreach (var sensorType in dynamicAssembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Sensor))))
                {
                    var sensor = (Sensor)Activator.CreateInstance(sensorType);
                    sensors.Add(sensor);
                }
            }
            return sensors;
        }

        public string DynamicAssemblyFolder
        {
            get { return ConfigurationManager.AppSettings["DynamicAssemblyFolder"]; }
        }
    }
}

然后只需创建引用CoreLibrary的程序集并将其dll放在已定义的文件夹中。例如......

using CoreLibrary;

namespace DynamicOne
{
    public class SensorA: Sensor
    {
        public override string Name
        {
            get { return  "Sensor A"; }
        }

        public override UnitOfMeasurement Unit
        {
            get { return null; }
        }

        public override int SamplingRate
        {
            get { return 10; }
        }

        public override CalibrationModel Calibration
        {
            get { return  null; }
        }
    }
}

并显示它适用于控制台应用程序。它仅引用CoreLibrary和DynamicClassLoader。它不需要引用动态加载的程序集,因为类型都是从Sensor派生的。

using System;
using DynamicClassLoader;

namespace DynamicLoading
{
    class Program
    {
        static void Main(string[] args)
        {
            var loader = new Loader();
            var sensors = loader.LoadDynamicSensors();
            Console.WriteLine(loader.DynamicAssemblyFolder);
            foreach (var sensor in sensors)
            {
                Console.WriteLine(sensor.Name);
            }
            Console.Read();
        }
    }
}

需要新类型时。创建一个新程序集,将其放入指定的文件夹并重新启动应用程序,然后加载新传感器并准备就绪。 (您可以使用文件观察器并自动执行此操作,但这可能有点过分。)

不确定如何使用传感器,但您可以在工厂中包装(或注入)装载器,以便轻松获得特定传感器。