泛型抽象類/泛型接口的妙用——派生類的統一封裝之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;}
  …… …… ……
}

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章