没有什么问题是不能通过增加一个抽象层解决的

起源

最近做FHIR(Fast Health Interoperability Resources)相关的项目,FHIR有很多个版本(STU3R4R5等).
在一个项目里面每个版本的.net包是不兼容的,虽然是不同的NuGet包名(Hl7.Fhir.R4.CoreHl7.Fhir.STU3.Core),但是模型都在一个命令空间下(Hl7.Fhir.Model).
如果同时引用两个编译器就会报错,同时自己使用的时候也不知道用的是哪个版本.
那么如何实现在一个项目里面,支持对两个FHIR版本包的操作???

反射反射,程序员的快乐

当然脑子里的第一个反应肯定是反射了,通过反射加载包,然后反射执行不同包的不同类的方法.

public static class FHIRMultiVersionUtility
{
        private static Dictionary<FHIRVersion, Assembly> fhirAssemblyDic = new Dictionary<FHIRVersion, Assembly>();

        static FHIRMultiVersionUtility()
        {
            fhirAssemblyDic.Add(FHIRVersion.R4, Assembly.LoadFrom(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "lib", "r4", "Hl7.Fhir.R4.Core.dll")));

            fhirAssemblyDic.Add(FHIRVersion.STU3, Assembly.LoadFrom(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "lib", "stu3", "Hl7.Fhir.STU3.Core.dll")));
        }
        public static string Serialize(object resource, ResourceFormat type, FHIRVersion version)
        {
            string fhirString = null;
            var assembly = fhirAssemblyDic[version];
            var serializerType = assembly.GetType($"Hl7.Fhir.Serialization.Fhir{type}Serializer");
            var serializer = Activator.CreateInstance(serializerType, new object[] { null });

            try
            {
                // xml json 序列化参数不一样,xml多一个root的参数
                var argsLen = type == ResourceFormat.Json ? 3 : 4;
                var args = new object[argsLen];
                args[0] = resource;
                args[1] = Enum.Parse(assembly.GetType("Hl7.Fhir.Rest.SummaryType"), "False");
                for (int i = 2; i < argsLen; i++)
                {
                    args[i] = null;
                }

                fhirString = (string)serializerType.GetMethod("SerializeToString").Invoke(serializer, args);
            }
            catch{}

            return fhirString;
        }    
}

以上便是一小段通过反射写的狗屎序列化代码...
当项目前期的时候,需求还比较好,还能通过反射愉快的玩耍,但是当需求越来越多,时间越来越久之后,这就会发现简直鬼都不认识的代码...

懂抽象的程序员才是真正的快乐

作为一个有追求的程序员,一定要竭力阻止屎山代码的形成,那要如何改进呢???
...thinking...
答案是抽象一层,然后在不同的类库项目里面实现它,不同的类库项目就可以引用不同的FHIR开发包了,项目中只引用抽象层,用工厂反射加载抽象的实现.

public interface IFHIRService
{
    FHIRVersion Version { get; }

    string Serialize(object resource, ResourceFormat type);
public static class FHIRServiceFactory
{
  private static Dictionary<FHIRVersion, IFHIRService> fhirServiceDictionary = new Dictionary<FHIRVersion, IFHIRService>();
  private static bool isInitial = false;

  public static void Initial()
  {
      if (isInitial)
          return;

      var libPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "lib");
      Directory.GetDirectories(libPath).ToList().ForEach(implementPath =>
      {
          var interfaceAssemblyName = typeof(FHIRServiceFactory).Assembly.GetName().Name.Replace("Abstractions", string.Empty);
          var fileInfo = Directory.GetFiles(implementPath).Select(f => new FileInfo(f))
          .FirstOrDefault(f => f.Name.StartsWith(interfaceAssemblyName) && f.Name != interfaceAssemblyName);
          if (fileInfo != null)
          {
              var assembly = Assembly.LoadFrom(fileInfo.FullName);
              var fhirServiceType = assembly.DefinedTypes.FirstOrDefault(t => typeof(IFHIRService).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
              if (fhirServiceType != null)
              {
                  var fhirService = (IFHIRService)Activator.CreateInstance(fhirServiceType);
                  if (fhirServiceDictionary.ContainsKey(fhirService.Version))
                  {
                      fhirServiceDictionary[fhirService.Version] = fhirService;
                  }
                  else
                  {
                      fhirServiceDictionary.Add(fhirService.Version, fhirService);
                  }
              }
          }
      });

      isInitial = true;
  }

  public static IFHIRService Create(FHIRVersion version)
  {
      if (fhirServiceDictionary.ContainsKey(version))
      {
          return fhirServiceDictionary[version];
      }

      return null;
  }
}
public class FHIRService : IFHIRService
{
    private FhirJsonSerializer jsonSerializer = new FhirJsonSerializer();
    private FhirXmlSerializer xmlSerializer = new FhirXmlSerializer();

    public FHIRVersion Version => FHIRVersion.R4;

    public string Serialize(object resource, ResourceFormat format)
    {
        if (format == ResourceFormat.Json)
        {
            return jsonSerializer.SerializeToString(resource);
        }
        else if (format == ResourceFormat.Xml)
        {
            return xmlSerializer.SerializeToString(resource);
        }

        return null;
    }
}

有了FHIRService这一层的抽象之后,就可以使用硬编码实现FHIR各个版本的实现了,再也不用恶心的反射了.
经过这次事件对抽象又有了一丝丝的领悟.

没有什么问题是不能通过增加一个抽象层解决的,如果有,在增加一层.   ----鲁迅.
Don't take it seriously!!!

posted @ 2020-08-31 09:23  whyfate  阅读(734)  评论(0编辑  收藏  举报