泛型抽象类/泛型接口的妙用——派生类的统一封装之HL7序列化和反序列化

先总结:泛型抽象类/泛型接口就是用抽象类和接口去约束派生类,但是为了统一实现方法,需要知道派生类的类型,然后传给基类,通过反射实现封装。

最近在做医疗设备的对接,就免不了接触HL7协议,相比于Json格式,它更节省数据长度,更适合与嵌入式系统的消息交互。
首先了解HL7的消息格式主要分为段和域,多个域组成一个段,多个段组成一个消息。
如下:

MSH|^~\&|LIS|TLA||202201011123||ORU^R01|||||<CR>
PID|||||||||||||||||||||||||||||||||||||||||<CR>
OBR|||||||||||||||||||||||||||||||||||||||||<CR>

域就是|之间的内容,也就是我们的字段,对照协议去解析具体数据,以|分隔。段就是MSH PID OBR,来代表消息头、患者信息、样本信息等,以回车符分隔,具体场景是:不同的消息是段自由组合的,一个段对应一个表,但是我们收到的是字符串,要转成HL7对象,再转换成数据模型,存到数据库。同样,我们需要把数据从数据库查询出来,然后数据模型转换成HL7对象,再转换成字符串。
那么我们的HL7消息对象的设计就是:

public class ORU_R01{
  public MSH MSH{get;set;}
  public PID PID{get;set;}
  public OBR OBR{get;set;}
}

public class MSH{
  public string A{get;set;}
  public string B{get;set;}
  …… …… ……
}

public class PID{
  public string A{get;set;}
  public string B{get;set;}
  …… …… ……
}
  • HL7序列化

要把ORU_R01 转换成HL7格式的字符串,就必须把MSH PID OBR 转换成字符串,然后以分隔,而把MSH、PID、OBR转换成字符串,就必须把他们的属性以|分隔,先初级封装一下,给他们加一个接口来约束

public interface IHL7
{
  string ToString();
}

这样就可以在MSH PID OBR 中这样写了:

public class MSH:IHL7 {
  public string A{get;set;}
  public string B{get;set;}
  …… …… ……
  public string ToString(){
    return $"{A}|{B}|{C}|……";
  }
}

public class PID{
  public string A{get;set;}
  public string B{get;set;}
  …… …… ……
  public string ToString(){
    return $"{A}|{B}|{C}|……";
  }
}

然后 ORU_R01就可以写成:

public class ORU_R01:IHL7{
  public MSH MSH{get;set;}
  public PID PID{get;set;}
  public OBR OBR{get;set;}
  public string ToString(){
    return $"{MSH.ToString()}\r{PID.ToString()}\r{OBR.ToString()}\r…………";
  }
}

这样在HL7序列化时,就不需要知道是ORU_R01还是MSH了,反正IHL7.ToString() 就可以了。
但是情况往往没这么简单,因为我们的消息会随着业务的增加而增加,ORU_R01、R02、R03………… 消息段也会不断的增加,每一个对象都实现一个ToString(),显然很麻烦,而且一旦有变化,每个类都需要修改,这很不简单。
这时候我们可以用一个泛型抽象去继承IHL7接口,并通过反射,统一实现所有的ToString():

public abstract BaseHL7<T>:IHL7 where T:IHL7
{
  //为了往后扩展,使用virtual
  protected virtual string ToString(char Separator='|') //默认以|分隔
  {
    var properties=GetType().GetProperties(BindingFlags.Instance|BindingFlags.Public|BindingFlags.DeclareOnly);//获取所有的属性
    var datas=properties.Select(t=>{
      dynamic itemdata=t.GetValue(this,null);
      return itemdata?.Tostring();
    });
  }
  
//因为object 自带 ToString() 所以不用实现接口的ToString(),不过这里我们需要调用ToString(char Separator='|'),所以可以有三种写法,三选一就行,调用IHL7的时候 都会执行。
1.显示实现IHL7.ToString()
string IHL7.ToString()
{
  ToString('|')
}
2.overrid object.ToString()
public overrid ToString()
{
  ToString('|')
}  
3. new ToString()
public new ToString()
{
  ToString('|')
}

}

有了BaseHL7,在ORU_R01、MSH、PID、OBR中就不需要实现ToString()了,当然了,鉴于ORU_R01是以分隔的,所以ORU_R01还是要override Tostring的,但是方便很多。

public class ORU_R01:BaseHL7<ORU_R01>{
  public MSH MSH{get;set;}
  public PID PID{get;set;}
  public OBR OBR{get;set;}
  public overried string ToString()
  {
    return ToString('\r');
  }
}

public class MSH:BaseHL7<MSH>{
  public string A{get;set;}
  public string B{get;set;}
  …… …… ……
}

public class PID:BaseHL7<PID>{
  public string A{get;set;}
  public string B{get;set;}
  …… …… ……
}

到此,我们的序列化就完成了。

posted @ 2022-09-20 15:15  咖啡不会醉  阅读(297)  评论(0编辑  收藏  举报