示例
示例情景
为了演示,设计了两个业务领域服务——“报价服务”和“联系人服务”,并为他们各自包装了一个独立的DTO对象,同时外层通过一个DTO集合进行集中管理。为了抽象的方便,根据应用情形定义如下抽象类型:
•将所有需要的业务数据对象定一个抽象的类型——BaseBusinessDomainObject。
•将所有DTO对象定一一个接口——IDataTransferObject。
•定义DTO集合,他的管理工作是基于抽象的IDataTransferObject进行的。
//C# BaseBusinessDomainObject
namespace VisionTask.Training.ServicePattern.UtilityService
{
public abstract class BaseBusinessDomainObject {}
}
//C# IDataTransferObject
using System.Xml;
namespace VisionTask.Training.ServicePattern.UtilityService
{
/// <summary>
/// 抽象的DTO 对象定义,包括“读/写”两个基本方法。
/// </summary>
public interface IDataTransferObject
{
/// <summary>
/// 用于DTO 集合中检索每个DTO 对象的名称。
/// </summary>
string Name { get;}
/// <summary>
/// 通过DTO 执行批量"读取/ 写入”单一消息内容结果。
/// </summary>
BaseBusinessDomainObject GetData();
void SetData(BaseBusinessDomainObject data);
}
}
//C# DataTransferObjectCollection
using System.Collections.Generic;
namespace VisionTask.Training.ServicePattern.UtilityService
{
/// <summary>
/// DTO 对象集合。
/// </summary>
public class DataTransferObjectCollection
{
private IDictionary<string, IDataTransferObject> dictionary =
new Dictionary<string, IDataTransferObject>();
public IDataTransferObject this[string name]
{
get
{
IDataTransferObject result;
if (!dictionary.TryGetValue(name, out result))
return null;
else
return result;
}
}
public void Add(IDataTransferObject dto) { dictionary.Add(dto.Name, dto); }
public void Remove(string name) { dictionary.Remove(name); }
}
}
实现简单的生产者服务
//C# QuoteService
using System;
using System.Xml;
using System.Web.Services;
using VisionTask.Training.ServicePattern.DataTransferObject;
namespace VisionTask.Training.ServicePattern.UtilityService
{
/// <summary>
/// 业务领域对象。
/// </summary>
public class Quote : BaseBusinessDomainObject
{
public struct QuoteItem
{
public string ProductId;
public double UnitPrice;
}
public string Id;
public string Company;
public QuoteItem[] Items;
}
/// <summary>
/// 可以提供业务领域对象的服务,但为了获得完整的对象信息,消费者
/// 服务需要多次调用服务方法,为此需要提供一个DTO 对象打包多次调用。
/// </summary>
[WebService]
public class QuoteService : WebService
{
/// <summary>
/// 演示用的临时数据。
/// 实际使用中该数据一般会从数据库或其他存储介质上获得。
/// </summary>
private static Quote quote = new Quote();
static QuoteService()
{
quote.Id = (new Random()).Next().ToString();
quote.Company = "vision task";
}
/// <summary>
/// 一组细颗粒度的服务方法。
/// </summary>
[WebMethod]
public string GetId() { return quote.Id; }
[WebMethod]
public string GetCompany() { return quote.Company; }
[WebMethod]
public void SetId(string id) { quote.Id = id; }
[WebMethod]
public void SetCompany(string company) { quote.Company = company; }
}
}
//C# ContactService
using System;
using System.Xml;
using System.Web.Services;
using VisionTask.Training.ServicePattern.DataTransferObject;
namespace VisionTask.Training.ServicePattern.UtilityService
{
/// <summary>
/// 业务领域对象。
/// </summary>
public class Contact : BaseBusinessDomainObject
{
public string Title;
public string Name;
public int Age;
}
/// <summary>
/// 可以提供业务领域对象的服务,但为了获得完整的对象信息,消费者
/// 服务需要多次调用服务方法,为此需要提供一个DTO 对象打包多次调用。
/// </summary>
[WebService]
public class ContactService : WebService
{
/// <summary>
/// 演示用的临时数据。
/// 实际使用中该数据一般会从数据库或其他存储介质上获得。
/// </summary>
private static Contact contact = new Contact();
static ContactService()
{
contact.Title = "S. Manager";
contact.Name = "joe";
contact.Age = 20;
}
/// <summary>
/// 一组细颗粒度的服务方法。
/// </summary>
[WebMethod]
public string GetTitle() { return contact.Title; }
[WebMethod]
public string GetName() { return contact.Name; }
[WebMethod]
public int GetAge() { return contact.Age; }
}
}
为生产者服务包装DTO对象
//C# QuoteDtoService
using System;
using System.Web.Services;
using System.Xml.Serialization;
using VisionTask.Training.ServicePattern.DataTransferObject;
using VisionTask.Training.ServicePattern.UtilityService.QuoteProducerService;
namespace VisionTask.Training.ServicePattern.UtilityService
{
/// <summary>
/// 面向报价服务的专用DTO对象。
/// 没有该对象时,消费者服务调用QuoteService获得一个完整的报价信息
/// 需要两次往返,通过该DTO 对象的打包,仅需要一次。
/// </summary>
[WebService]
public class QuoteDtoService : WebService, IDataTransferObject
{
public string Name { get { return "Quote"; } }
[WebMethod]
[XmlInclude(typeof(Quote))]
public BaseBusinessDomainObject GetData()
{
Quote quote = new Quote();
QuoteService service = new QuoteService();
quote.Id = service.GetId();
quote.Company = service.GetCompany();
return quote;
}
[WebMethod]
[XmlInclude(typeof(Quote))]
public void SetData(BaseBusinessDomainObject data)
{
if (data == null) throw new NullReferenceException();
Quote quote = (Quote)data;
QuoteService service = new QuoteService();
service.SetId(quote.Id);
service.SetCompany(quote.Company);
}
}
}
//Unit Test:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionTask.Training.ServicePattern.UtilityService;
namespace VisionTask.Training.ServicePattern.UtilityService.UnitTest
{
[TestClass()]
public class QuoteDtoServiceTest
{
[TestMethod]
public void Test()
{
// DTO
IDataTransferObject dto = new QuoteDtoService();
Quote quote = (Quote)dto.GetData();
Assert.AreEqual<string>("vision task", quote.Company);
}
}
}
//C# ContactDtoService
using System;
using System.Web.Services;
using System.Xml.Serialization;
using VisionTask.Training.ServicePattern.DataTransferObject;
using VisionTask.Training.ServicePattern.UtilityService.ContactProducerService;
namespace VisionTask.Training.ServicePattern.UtilityService
{
/// <summary>
/// 面向联系人服务的专用DTO对象。
/// 没有该对象时,消费者服务调用ContactService 获得一个完整的联系人信息.
/// 需要三次往返,通过该DTO 对象的打包,仅需要一次。
/// </summary>
[WebService]
public class ContactDtoService : WebService, IDataTransferObject
{
public string Name { get { return "Contact"; } }
[WebMethod]
[XmlInclude(typeof(Contact))]
public BaseBusinessDomainObject GetData()
{
Contact contact = new Contact();
ContactService service = new ContactService();
contact.Title = service.GetTitle();
contact.Name = service.GetName();
contact.Age = service.GetAge();
return contact;
}
[WebMethod]
public void SetData(BaseBusinessDomainObject data)
{
/// 暂时不支持回写服务。
throw new NotSupportedException();
}
}
}
//Unit Test:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionTask.Training.ServicePattern.UtilityService.UnitTest.ContactService;
using VisionTask.Training.ServicePattern.UtilityService;
namespace VisionTask.Training.ServicePattern.UtilityService.UnitTest
{
[TestClass()]
public class ContactDtoServiceTest
{
[TestMethod]
public void Test()
{
// DTO
IDataTransferObject dto = new ContactDtoService();
Contact contact = (Contact)dto.GetData();
Assert.AreEqual<string>("S. Manager", contact.Title);
Assert.AreEqual<string>("joe", contact.Name);
Assert.AreEqual<int>(20, contact.Age);
}
}
}
使用DTO集合的测试
//Unit Test:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionTask.Training.ServicePattern.UtilityService.UnitTest.ContactService;
using VisionTask.Training.ServicePattern.UtilityService;
namespace VisionTask.Training.ServicePattern.UtilityService.UnitTest
{
[TestClass()]
public class DataTransferObjectCollectionTest
{
[TestMethod]
public void Test()
{
// 模拟初始时 DTO 集合的构造过程。
DataTransferObjectCollection dtos = new DataTransferObjectCollection();
dtos.Add(new QuoteDtoService());
dtos.Add(new ContactDtoService());
// 消费者服务通过 DTO 集合获取需要的 DTO 对象。
IDataTransferObject dto = dtos["Contact"];
Contact contact = (Contact)dto.GetData();
Assert.AreEqual<string>("S. Manager", contact.Title);
Assert.AreEqual<string>("joe", contact.Name);
Assert.AreEqual<int>(20, contact.Age);
}
}
}
小结
实际项目中,精简模式的DTO模式往往是最容易遭到开发人员反对的一个模式,因为它简单到就是简单的read / write,是否使用DTO模式最主要的因素在于权衡效率与工作量后的结果。
//C# BaseBusinessDomainObject