收集器、转换器和格式化器模式
收集器、转换器和格式化器模式初学者指南
引言
本文将向大家介绍收集器、转换器和格式化器模式。在深入探讨该模式之前,我假设目标读者对实现标准设计模式有一些经验。此外,对面向对象原则和实践有透彻的理解。
如果你有兴趣学习和实现一种机制来收集数据、应用转换器来修改内容以及应用格式化器来格式化数据,那么你就是本文的合适读者。众所周知,设计模式是解决常见重复问题的方案。今天,我们将解决数据收集、转换和格式化最常见的实践。真实世界场景
让我们通过一个例子来理解真实世界的场景。
- 考虑一个`DateTime`对象的例子。它保存实际的`DateTime`值,但当涉及到`DateTime`的格式化时,例如你可能通过指定格式字符串、`IFormatProvider`或它们的组合来使用`DateTime`的`ToString`方法以返回特定格式的日期。在内部,`DateTime`类的`ToString`方法使用一个名为“`DateTimeFormat`”的内部类来格式化`DateTime`值。请查看http://www.dotnetperls.com/datetime-format以了解`DateTime`格式化。
- 以医疗保健信息交换(HIE)产品为例,你希望以可互操作的方式传输或交换患者的医疗保健信息。这意味着,你希望以特定格式(如CCR、CCD、CDA和FHIR等)导出患者数据。想象一下,你有一个数据库,其中存储了所有患者、提供者和相关的患者健康信息。在这种情况下,数据的消费者不受特定数据格式的约束,而是根据行业指定格式接受患者健康信息。
负责交换健康信息的软件应该能够支持客户的需求。这时就需要数据收集器和格式化器。数据库是存储患者数据的介质。可以根据客户需求收集、转换和转换数据。
设计模式的高级概述
注意:以下格式化程序类型仅用于演示目的。
收集器
“`收集器`”是一个负责收集数据的实体。你的数据可能存储在平面文件、数据库或任何格式中。最终,收集器的作用是收集这些数据。定义清晰的边界上下文很重要。收集器看起来像数据访问组件。在某些情况下,它可能不是。因此,最好将收集器与数据访问组件分开。
转换器
“`转换器`”是一个负责修改或更改收集器收集的数据的实体。该实体负责处理收集到的数据。有时你需要根据客户要求执行翻译或应用通用翻译。所有通用和客户端特定的翻译规则都应作为转换器。你不需要为每个收集器都使用转换器。注意 - 转换器是可选实体。它取决于你所处理的领域。
Formatter
“`格式化器`”是一个负责格式化收集器收集的数据或转换后的数据的实体。“格式化器”根据客户需求将数据转换为特定格式。格式化器的一个例子是将收集器收集的数据转换为JSON或XML格式。
解耦收集器、转换器和格式化器的必要性
让我们试着理解解耦收集器、转换器和格式化器的需求或必要性。我们将实现以下目标。
松耦合,并具有收集、翻译和格式化数据的单一职责。
- 有时,在处理数据转换时。将它们分开始终是个好主意。这时就出现了“`转换器`”的作用。
- 格式化器不必依赖收集器。这意味着收集器和格式化器可以共存,并且可以独立修改,而互不影响。
- 依赖性越小,越容易增强或扩展收集器、转换器或格式化器的现有行为。你可以自由地扩展格式化器以支持额外的客户端需求。
背景
对标准设计模式的了解和理解。
使用代码
让我们看一下示例代码,了解如何实现该模式。出于演示目的,我将以Open Dental中自定义的健康级别7(HL7)消息为例。在收集器中,我们将收集一个包含“`Message`”实体的HL7消息对象。此外,“`Message`”实体由“`MessageHeader`”、“`PatientIdentification`”、“`Guarantor`”和“`Insurance`”实体组成。
public class HL7MessageRoot { public MessageMessage { get; set; } } public classMessage { public MessageHeaderMessageHeader { get; set; } public PatientIdentificationPatientIdentification { get; set; } public GuarantorGuarantor { get; set; } public InsuranceInsurance { get; set; } }
出于演示目的,我们不会访问数据库并构建HL7消息对象。相反,我们将返回一个模拟对象。下面是其高级代码片段。
public classMockHL7Message { public HL7MessageRoot GetHL7Message() { return new HL7MessageRoot { Message = GetMessage() }; } MessageGetMessage() { return newMessage { MessageHeader = GetMessageHeader(), PatientIdentification = GetPatientIdentification(), Guarantor = GetGuarantor(), Insurance = GetInsurance() }; } . . . }
这是我们收集器的代码片段。
public class HL7MessageCollector { public HL7MessageRoot Collect() { // Return Mock Data for now return new MockHL7Message().GetHL7Message(); } }
现在让我们讨论“`转换器`”。我们将有两个。一个用于格式化电话号码,另一个用于屏蔽SSN号码。下面是其代码片段。
public class PhoneNumberTranslator: ITranslate { public string Translate(stringphoneNumber) { return phoneNumber.ToString() .Replace("-", "") .Replace("(", "") .Replace(")", ""); } } //Reused code - http://stackoverflow.com/questions/5254197/format-ssn-using-regex public class SSNMaskTranslator: ITranslate { publicstring Translate(stringoriginalSSN) { string ssn = originalSSN.ToString(); if (ssn.Length < 5) originalSSN = ssn; var trailingNumbers = ssn.Substring(ssn.Length - 4); var leadingNumbers = ssn.Substring(0, ssn.Length - 4); var maskedLeadingNumbers = Regex.Replace(leadingNumbers, @ "[0-9]", "X"); return maskedLeadingNumbers + trailingNumbers; } }
现在我们解决方案的最后一个重要部分是格式化器。作为演示示例的一部分,我们将有两种格式化器。一种用于将HL7消息格式化为XML,另一种用于JSON。下面是`HL7JsonFormatter`的代码片段,我们利用JSON.NET来序列化HL7消息对象。为了在控制台上显示JSON输出,我使用了可选参数Formatting.Indented。
public class HL7JsonFormatter: IHL7Formatter { public string FormatHL7Message(HL7MessageRoot hL7MessageRoot) { return JsonConvert.SerializeObject(hL7MessageRoot, Formatting.Indented); } }
下面是`HL7XMLFormatter`的代码片段。“`FormatHL7Message`”方法接受一个HL7消息根对象,并负责通过创建XML文档并为“`MessageHeader`”、“`PatientIdentification`”、“`Gaurantor`”和“`Insurance`”添加XML元素来构建基于XML的HL7消息。
public class HL7XMLFormatter: IHL7Formatter { public string FormatHL7Message(HL7MessageRoot hL7MessageRoot) { XDocumentxDocument = new XDocument(); var messageElement = new XElement("Message"); messageElement.Add(GetMessageHeader(hL7MessageRoot)); messageElement.Add(GetPatientIdentification(hL7MessageRoot)); messageElement.Add(GetGuarantor(hL7MessageRoot)); messageElement.Add(GetInsurance(hL7MessageRoot)); xDocument.Add(messageElement); StringBuilderstringBuilder = new StringBuilder(); using(TextWriter writer = new StringWriter(stringBuilder)) { xDocument.Save(writer); } returnstringBuilder.ToString(); } XElementGetMessageHeader(HL7MessageRoot hL7MessageRoot) { return new XElement("MessageHeader", new XElement("DateTimeOfMessage", hL7MessageRoot.Message.MessageHeader.DateTimeOfMessage), new XElement("MessageType", hL7MessageRoot.Message.MessageHeader.MessageType), new XElement("OpenDentalVersion", hL7MessageRoot.Message.MessageHeader.OpenDentalVersion)); } XElementGetPatientIdentification(HL7MessageRoot hL7MessageRoot) { return new XElement("PatientIdentification", newXElement("NameLast", hL7MessageRoot.Message.PatientIdentification.NameLast), newXElement("NameFirst", hL7MessageRoot.Message.PatientIdentification.NameFirst), newXElement("NameMiddle", hL7MessageRoot.Message.PatientIdentification.NameMiddle), newXElement("DateOfBirth", hL7MessageRoot.Message.PatientIdentification.DateOfBirth), newXElement("Sex", hL7MessageRoot.Message.PatientIdentification.Sex), newXElement("AliasFirst", hL7MessageRoot.Message.PatientIdentification.AliasFirst), newXElement("AddressStreet", hL7MessageRoot.Message.PatientIdentification.AddressStreet), newXElement("AddressOtherDesignation", hL7MessageRoot.Message.PatientIdentification.AddressOtherDesignation), newXElement("AddressCity", hL7MessageRoot.Message.PatientIdentification.AddressCity), newXElement("AddressStateOrProvince", hL7MessageRoot.Message.PatientIdentification.AddressStateOrProvince), newXElement("AddressZipOrPostalCode", hL7MessageRoot.Message.PatientIdentification.AddressZipOrPostalCode), newXElement("PhoneHome", hL7MessageRoot.Message.PatientIdentification.PhoneHome), newXElement("EmailAddressHome", hL7MessageRoot.Message.PatientIdentification.EmailAddressHome), newXElement("PhoneBusiness", hL7MessageRoot.Message.PatientIdentification.PhoneBusiness), newXElement("MaritalStatus", hL7MessageRoot.Message.PatientIdentification.MaritalStatus), newXElement("SSN", hL7MessageRoot.Message.PatientIdentification.SSN), newXElement("NotePhoneAddress", hL7MessageRoot.Message.PatientIdentification.NotePhoneAddress), newXElement("NoteMedicalComplete", hL7MessageRoot.Message.PatientIdentification.NoteMedicalComplete)); } XElementGetGuarantor(HL7MessageRoot hL7MessageRoot) { returnnewXElement("Guarantor", newXElement("NameLast", hL7MessageRoot.Message.Guarantor.NameLast), newXElement("NameFirst", hL7MessageRoot.Message.Guarantor.NameFirst), newXElement("NameMiddle", hL7MessageRoot.Message.Guarantor.NameMiddle), newXElement("AddressStreet", hL7MessageRoot.Message.Guarantor.AddressStreet), newXElement("AddressOtherDesignation", hL7MessageRoot.Message.Guarantor.AddressOtherDesignation), newXElement("AddressCity", hL7MessageRoot.Message.Guarantor.AddressCity), newXElement("AddressStateOrProvince", hL7MessageRoot.Message.Guarantor.AddressStateOrProvince), newXElement("AddressZipOrPostalCode", hL7MessageRoot.Message.Guarantor.AddressZipOrPostalCode), newXElement("PhoneHome", hL7MessageRoot.Message.Guarantor.PhoneHome), newXElement("EmailAddressHome", hL7MessageRoot.Message.Guarantor.EmailAddressHome), newXElement("PhoneBusiness", hL7MessageRoot.Message.Guarantor.PhoneBusiness), newXElement("DateOfBirth", hL7MessageRoot.Message.Guarantor.DateOfBirth), newXElement("Sex", hL7MessageRoot.Message.Guarantor.Sex), newXElement("GuarantorRelationship", hL7MessageRoot.Message.Guarantor.GuarantorRelationship), newXElement("SSN", hL7MessageRoot.Message.Guarantor.SSN), newXElement("EmployerName", hL7MessageRoot.Message.Guarantor.EmployerName), newXElement("MaritalStatus", hL7MessageRoot.Message.Guarantor.MaritalStatus)); } XElementGetInsurance(HL7MessageRoot hL7MessageRoot) { returnnewXElement("Insurance", newXElement("CompanyName", hL7MessageRoot.Message.Insurance.CompanyName), newXElement("AddressStreet", hL7MessageRoot.Message.Insurance.AddressStreet), newXElement("AddressOtherDesignation", hL7MessageRoot.Message.Insurance.AddressOtherDesignation), newXElement("AddressCity", hL7MessageRoot.Message.Insurance.AddressCity), newXElement("AddressStateOrProvince", hL7MessageRoot.Message.Insurance.AddressStateOrProvince), newXElement("AddressZipOrPostalCode", hL7MessageRoot.Message.Insurance.AddressZipOrPostalCode), newXElement("PhoneNumber", hL7MessageRoot.Message.Insurance.PhoneNumber), newXElement("GroupNumber", hL7MessageRoot.Message.Insurance.GroupNumber), newXElement("GroupName", hL7MessageRoot.Message.Insurance.GroupName), newXElement("InsuredGroupEmpName", hL7MessageRoot.Message.Insurance.InsuredGroupEmpName), newXElement("PlanEffectiveDate", hL7MessageRoot.Message.Insurance.PlanEffectiveDate), newXElement("PlanExpirationDate", hL7MessageRoot.Message.Insurance.PlanExpirationDate), newXElement("InsuredsNameLast", hL7MessageRoot.Message.Insurance.InsuredsNameLast), newXElement("InsuredsNameFirst", hL7MessageRoot.Message.Insurance.InsuredsNameFirst), newXElement("InsuredsNameMiddle", hL7MessageRoot.Message.Insurance.InsuredsNameMiddle), newXElement("InsuredsRelationToPat", hL7MessageRoot.Message.Insurance.InsuredsRelationToPat), newXElement("InsuredsDateOfBirth", hL7MessageRoot.Message.Insurance.InsuredsDateOfBirth), newXElement("InsuredsAddressStreet", hL7MessageRoot.Message.Insurance.InsuredsAddressStreet), newXElement("InsuredsAddressOtherDesignation", hL7MessageRoot.Message.Insurance.InsuredsAddressOtherDesignation), newXElement("InsuredsAddressCity", hL7MessageRoot.Message.Insurance.InsuredsAddressCity), newXElement("InsuredsAddressStateOrProvince", hL7MessageRoot.Message.Insurance.InsuredsAddressStateOrProvince), newXElement("InsuredsAddressZipOrPostalCode", hL7MessageRoot.Message.Insurance.InsuredsAddressZipOrPostalCode), newXElement("AssignmentOfBenefits", hL7MessageRoot.Message.Insurance.AssignmentOfBenefits), newXElement("ReleaseInformationCode", hL7MessageRoot.Message.Insurance.ReleaseInformationCode), newXElement("PolicyNumber", hL7MessageRoot.Message.Insurance.PolicyNumber), newXElement("PolicyDeductible", hL7MessageRoot.Message.Insurance.PolicyDeductible), newXElement("PolicyLimitAmount", hL7MessageRoot.Message.Insurance.PolicyLimitAmount), newXElement("InsuredsSex", hL7MessageRoot.Message.Insurance.InsuredsSex), newXElement("InsuredsSSN", hL7MessageRoot.Message.Insurance.InsuredsSSN), newXElement("InsuredsPhoneHome", hL7MessageRoot.Message.Insurance.InsuredsPhoneHome), newXElement("NotePlan", hL7MessageRoot.Message.Insurance.NotePlan)); } }
这是`HL7MessageFormatter`的代码片段,它接受`HL7MessageRoot`对象并根据指定的格式化程序格式化为HL7消息。
public class HL7MessageFormatter { HL7MessageRoot hL7MessageRootObject; public HL7MessageFormatter(HL7MessageRoot hL7MessageRoot) { hL7MessageRootObject = hL7MessageRoot; } public string Format(IHL7Formatter formatter) { return formatter.FormatHL7Message(hL7MessageRootObject); } }
让我们看一下主代码,看看如何连接收集器、转换器和格式化器。下面是相同的代码片段。首先我们收集数据,然后我们将转换电话号码、SSN掩码等。最后,我们通过使用我们构建的格式化器来调用将HL7消息对象格式化为XML和JSON。
static void Main(string[] args) { // Collect or Gather Data var hL7MessageCollector = newHL7MessageCollector(); var hL7MessageRoot = hL7MessageCollector.Collect(); // Translate data Translate(hL7MessageRoot); // Format and display as XML Console.WriteLine("Display HL7 message as XML\n"); varxmlOutput = FormatToXML(hL7MessageRoot); Console.WriteLine(xmlOutput); // Format and display as JSON Console.WriteLine("\nDisplay HL7 message as JSON\n"); varjsonOutput = FormatToJSON(hL7MessageRoot); Console.WriteLine(jsonOutput); Console.ReadLine(); }
这是格式化 HL7 消息对象的代码片段。
static stringFormatToXML(HL7MessageRoot hL7MessageRoot) { var hL7MessageFormatter = newHL7MessageFormatter(hL7MessageRoot); return hL7MessageFormatter.Format(newHL7XMLFormatter()); } static stringFormatToJSON(HL7MessageRoot hL7MessageRoot) { var hL7JSONFormatter = newHL7MessageFormatter(hL7MessageRoot); return hL7JSONFormatter.Format(newHL7JsonFormatter()); }
下面是“`转换器`”的代码片段,我们利用`PhoneNumberTranslator`通过移除连字符和括号来翻译或更改电话号码。`SSNMaskTranslator`用于掩码SSN号码。
static void Translate(HL7MessageRoot hL7MessageRoot) { var phoneNumber = hL7MessageRoot.Message.PatientIdentification.PhoneHome; var phoneNumberTranslator = newPhoneNumberTranslator(); if (!string.IsNullOrEmpty(phoneNumber)) { hL7MessageRoot.Message.PatientIdentification.PhoneHome = phoneNumberTranslator.Translate(phoneNumber); } var ssn = hL7MessageRoot.Message.PatientIdentification.SSN; var ssnMaskTranslator = newSSNMaskTranslator(); if (!string.IsNullOrEmpty(ssn)) { hL7MessageRoot.Message.PatientIdentification.SSN = ssnMaskTranslator.Translate(ssn); } ssn = hL7MessageRoot.Message.Guarantor.SSN; if (!string.IsNullOrEmpty(ssn)) { hL7MessageRoot.Message.Guarantor.SSN = ssnMaskTranslator.Translate(ssn); } }
HL7 模型类图
转换器类图
下面是转换器类的类图。
注意: 以下类是示例应用程序的一部分,转换器是可选的,纯粹取决于您正在处理的领域。
格式化器类图
HL7 消息(XML 格式)
这是HL7消息如何格式化为XML的屏幕截图。
HL7 消息(JSON 格式)
这是HL7消息如何格式化为JSON的屏幕截图。
关注点
尽管这只是尝试创建解决特定类型问题设计模式的一小步,但我很高兴分享我长期以来的想法。我真的很兴奋:)
欢迎开放和建设性的批评!
最新代码也可在 - https://github.com/ranjancse26/CollectorFormatterSample 找到
历史
版本 1.0 - 初稿于 2015 年 3 月 2 日发布到 CP。