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


    起源

    最近做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!!!

  • 相关阅读:
    Python中的yield详解
    Python脚本实现图片加水印
    ajax
    商城页面的增删改查
    事务及完成转账功能
    DBUtils和完善商城页面
    EL和jstl技术
    JSP
    jquery插件
    Cookie和Session
  • 原文地址:https://www.cnblogs.com/wh-blog/p/13587874.html
Copyright © 2020-2023  润新知