在抽象工厂中使用依赖倒置
本篇实现的效果为:当学校有通知的时候,把通知内容发送到每一个人,如下:
□ 思路
可以把通知内容、通知对象、通知方式、通知行为抽象成类、接口、接口实现,再为一些接口创建对应的抽象工厂及其实现。所有的这些依赖可以通过"依赖倒置容器"来管理。
→关于通知内容,可以封装成一个类
→关于通知对象,可以抽象出基类和实现类
→关于通知对象的Repository,有对应的接口、接口实现,抽象工厂、抽象工厂实现
→关于Email通知方式的Repository,有对应的接口,接口实现,抽象工厂、抽象工厂实现
→关于通知的行为,可以有对应的接口和接口实现
→关于客户端调用,这么些接口和接口实现,以及抽象工厂和抽象工厂实现,我们需要使用"依赖倒置",把所有的依赖注册到依赖倒置容器中,使用的时候,再分别拿出来。
另外,可以为所有的抽象工厂提供一个统一的访问入口,让一个接口实现所有的抽象工厂接口,外部访问的时候,可以通过这个接口拿到所有的抽象工厂。
□ 关于通知内容
可以把通知的对象抽象出一个基类来,学生、老师等继承于这个基类。
using System; namespace AbstractFactoryDI.Core.Model { public abstract class Person { public virtual Guid Id { get; set; } public virtual string PIN { get; set; } public virtual string Name { get; set; } public virtual string Surname { get; set; } public virtual string Email { get; set; } } public partial class Student : Person { } public partial class Trainer : Person { } }
□ 关于通知对象的Repository
关于通知对象Person的Repository接口,从中可以获取到通知对象Person的集合。
using System.Collections.Generic; using AbstractFactoryDI.Core.Model; namespace AbstractFactoryDI.Core.Repository { public interface IPersonRepository { IEnumerable<Person> GetAllActivePeople(); } }
为通知对象Person的Repository接口创建抽象工厂。
using AbstractFactoryDI.Core.Repository; namespace AbstractFactoryDI.Core.Factories { public interface IRepositoryFactory { IPersonRepository CreatePersonRepository(); } }
针对IPersonRepository的实现是:
using System; using System.Collections.Generic; using System.Linq; using AbstractFactoryDI.Core.Model; using AbstractFactoryDI.Core.Repository; namespace AbstractFactoryDI.Infrastructure.Repository { public class PersonRepository : IPersonRepository { private static ICollection<Person> _people = new List<Person>() { new Student(){Id = Guid.NewGuid(),Name = "jack", Surname = "cute jack",Email = "jack@sina.com", PIN = "001"}, new Student(){Id = Guid.NewGuid(),Name = "sunny", Surname = "cute sunny",Email = "sunny@sina.com", PIN = "002"}, }; public System.Collections.Generic.IEnumerable<Core.Model.Person> GetAllActivePeople() { return _people.AsEnumerable(); } } }
□ 关于通知方式中的Email通知
把通过Email通知的方式也抽象成一个接口。
using System; namespace AbstractFactoryDI.Core.Notificators { public interface IEmailNotificator : IDisposable { void SendNotification(string toAddress, string message); } }
与IEmailNotificator对应的抽象工厂为:
using AbstractFactoryDI.Core.Notificators; namespace AbstractFactoryDI.Core.Factories { public interface IEmailNotificatorFactory { IEmailNotificator CreateEmailNotificator(); } }
IEmailNotificator接口实现是:
using System; using AbstractFactoryDI.Core.Notificators; namespace AbstractFactoryDI.Infrastructure.Notificators { public class EmailNotificator : IEmailNotificator { public void SendNotification(string toAddress, string message) { //System.Net.Mail.SmtpClient发邮件 Console.WriteLine(message); Console.WriteLine(); Console.WriteLine(); } public void Dispose() { //dispose System.Net.Mail.SmtpClient } } }
□ 关于抽象工厂的统一入口
现在已经有了有关通知对象的抽象工厂IRepositoryFactory和有关邮件通知方式的抽象工厂IEmailNotificatorFactory,我们还希望为所有的这些抽象工厂创建一个统一的访问入口,于是创建实现所有抽象工厂接口的接口:
namespace AbstractFactoryDI.Core.Factories { public interface IServiceFactory : IRepositoryFactory, IEmailNotificatorFactory { } }
IServiceFactory对应的实现是:
using AbstractFactoryDI.Core.Factories; using AbstractFactoryDI.Infrastructure.Notificators; using AbstractFactoryDI.Infrastructure.Repository; namespace AbstractFactoryDI.Factories.Concrete { public class ServiceFactory : IServiceFactory { public virtual Core.Repository.IPersonRepository CreatePersonRepository() { return new PersonRepository(); } public virtual Core.Notificators.IEmailNotificator CreateEmailNotificator() { return new EmailNotificator(); } } }
□ 关于通知内容
通知内容包含放假开始时间、结束时间、内容等,把之封装成一个类。
using System; namespace AbstractFactoryDI.Core.Services { public class ClosureInfo { public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public string Reason { get; set; } } }
□ 关于通知行为
发送通知需要以上的ClosureInfo类型参数,考虑到发送通知的方式可能有多种,比如短信、邮件等,可以为发送通知抽象出一个接口。
namespace AbstractFactoryDI.Core.Services { public interface INotificationService { void NotifySchoolClosure(ClosureInfo info); } }
关于INotificationService的实现。我们可以从抽象工厂统一入口拿到IPersonRepository,从而获取到所有的通知对象。还可以从抽象工厂统一入口拿到IEmailNotificator,来发送通知信息。
using System.Collections.Generic; using System.Text; using AbstractFactoryDI.Core.Factories; using AbstractFactoryDI.Core.Model; namespace AbstractFactoryDI.Core.Services { public class NotificationService : INotificationService { private readonly IServiceFactory _factory; public NotificationService(IServiceFactory factory) { _factory = factory; } public void NotifySchoolClosure(ClosureInfo info) { var personRepository = _factory.CreatePersonRepository(); var people = personRepository.GetAllActivePeople(); NotifySchoolClosure(info, people); } private void NotifySchoolClosure(ClosureInfo info, IEnumerable<Person> people) { using (var notificator = _factory.CreateEmailNotificator()) { foreach (var person in people) { notificator.SendNotification(person.Email, FormatMessage(person, info)); } } } private string FormatMessage(Person person, ClosureInfo info) { var str = new StringBuilder(); str.Append(string.Format("您好 {0}\n", person.Name)); str.Append(info.Reason); str.Append(string.Format(" 学校将在{0}和{1}期间放假\n", info.StartDate, info.EndDate)); str.Append("请注意安全~~"); return str.ToString(); } } }
□ 客户端
前面已经有了抽象工厂以及抽象工厂的实现,抽象工厂入口以及抽象工厂入口的实现,接口和接口实现......所有的这些,都可以让依赖倒置来解决,这里使用Microsoft.Practices.Unity。
using AbstractFactoryDI.Core.Factories; using AbstractFactoryDI.Core.Services; using AbstractFactoryDI.Factories.Concrete; using Microsoft.Practices.Unity; namespace AbstractFactoryDI.Client { public class RootContainer { private static readonly UnityContainer _container; static RootContainer() { _container = new UnityContainer(); RegisterAll(); } internal static void RegisterAll() { _container.RegisterType<IEmailNotificatorFactory, ServiceFactory>(); _container.RegisterType<IRepositoryFactory, ServiceFactory>(); _container.RegisterType<IServiceFactory, ServiceFactory>(); _container.RegisterType<INotificationService, NotificationService>(); } internal static Iofs Resolve<Iofs>() { return _container.Resolve<Iofs>(); } } }
客户端调用的时候,首先在构造函数中注册所有的依赖,然后在使用的时候,在从依赖倒置容器中解析出具体的接口实现。
using System; using AbstractFactoryDI.Core.Services; namespace AbstractFactoryDI.Client { class Program { public Program() { RootContainer.RegisterAll(); } static void Main(string[] args) { NotifyChristmasVacation(); Console.ReadKey(); } static void NotifyChristmasVacation() { INotificationService notificaitonService = RootContainer.Resolve<INotificationService>(); var closureInfo = new ClosureInfo() { StartDate = new DateTime(DateTime.Now.Year, 12, 24), EndDate = new DateTime(DateTime.Now.Year, 12, 26), Reason = "圣诞节放假~~" }; notificaitonService.NotifySchoolClosure(closureInfo); } } }
参考资料: