面向对象设计 (OOD) 有助于我们开发出高性能, 易扩展以及易复用的程序. 其中, OOD 有一个重要的思想那就是依赖倒置原则(DIP).
依赖倒置原则(DIP): 一种软件架构设计的原则(抽象概念)
控制反转(IoC): 一种反转流, 依赖和接口的方式(DIP 的具体实现方式)
依赖注入(DI):IoC 的一种实现方式, 用来反转依赖(IoC 的具体实现方式)
IoC 容器: 依赖注入的框架, 用来映射依赖, 管理对象创建和生存周期(DI 框架),
Unity: 是微软推出的 IOC 框架
TDD: 测试驱动开发, 是敏捷开发中的一项核心实践和技术, 也是一种设计方法论
上面的内容都是抄的(英文不好, 放在这里备查), 相关的内容看了一些, 不明觉厉.
曾经写了一个酒店读写房卡的程序, 不同的酒店门锁是不一样的, 同一个酒店也有可能更换门锁, 程序流程:
1. 通过 Api 获取当前酒店的门锁类型
2. 如果需要则下载对应门锁的 dll
3. 实现读写功能
一, 定义接口
新建一个类库(Lock.Interface), 代码如下:
- namespace Lock
- {
- public interface ILock
- {
- /// <summary>
- /// 门锁初始化
- /// </summary>
- /// <param name="password">初始化密码</param>
- /// <returns></returns>
- bool Init(int password);
- }
- }
是不是算是 IoC 了?(我不确定)
二, 单元测试
新建一个单元测试项目(Lock.Tests), 代码如下:
1. 门锁 A 单元测试
- /// <summary>
- /// 门锁 A 测试
- /// </summary>
- [TestClass()]
- public class LockATests
- {
- /// <summary>
- /// 初始化, password 为正奇数
- /// </summary>
- [TestMethod()]
- public void InitTest()
- {
- ILock l = new LockA();
- var ret = l.Init(1);
- Assert.AreEqual(ret, true);
- ret = l.Init(2);
- Assert.AreEqual(ret, false);
- ret = l.Init(-1);
- Assert.AreEqual(ret, false);
- }
- }
2. 门锁 B 单元测试
- /// <summary>
- /// 门锁 B 测试
- /// </summary>
- [TestClass()]
- public class LockBTests
- {
- /// <summary>
- /// 初始化测试, password 为正偶数
- /// </summary>
- [TestMethod()]
- public void InitTest()
- {
- ILock l = new LockB();
- var ret = l.Init(1);
- Assert.AreEqual(ret, false);
- ret = l.Init(2);
- Assert.AreEqual(ret, true);
- ret = l.Init(-1);
- Assert.AreEqual(ret, false);
- }
显然编译都不能通过(不能通过编译也是一种测试)
三, 定义类
1. 创建门锁 A 类库(Lock.LockA), 代码如下:
- /// <summary>
- /// 门锁 A
- /// </summary>
- public class LockA : ILock
- {
- /// <summary>
- /// 初始化
- /// </summary>
- /// <param name="password">正确密码是正奇数</param>
- /// <returns></returns>
- public bool Init(int password)
- {
- return password> 0 && (password % 2) == 1;
- }
- }
2. 创建门锁 B 类库(Lock.LockB), 代码如下:
- /// <summary>
- /// 门锁 B
- /// </summary>
- public class LockB : ILock
- {
- /// <summary>
- /// 初始化
- /// </summary>
- /// <param name="password">正确密码是正偶数</param>
- /// <returns></returns>
- public bool Init(int password)
- {
- return password> 0 && (password % 2) == 0;
- }
- }
分别编译成功后, 回到测试项目, 添加引用, 编译通过, 运行测试:
算不算 TDD?
四, 主程序
1. 添加控制台项目
2. 添加 Unity(5.8.6)包
3. 修改 App.Config 为:
- <?xml version="1.0" encoding="utf-8" ?>
- <configuration>
- <configSections>
- <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Unity.Configuration, Version=5.2.1.0, Culture=neutral, PublicKeyToken=6d32ff45e0ccc69f" />
- <!-- 上一些的内容网上复制过来有错误, 后来查看 UnityConfigurationSection 的定义, 复制了对应的程序集
- type="命名空间. 类名, 程序集"
- -->
- </configSections>
- <unity>
- <containers>
- <container name="LockA">
- <register type="Lock.ILock,Lock.Interface" mapTo="Lock.LockA,Lock.LockA"></register>
- </container>
- <container name="LockB">
- <register type="Lock.ILock,Lock.Interface" mapTo="Lock.LockB,Lock.LockB"></register>
- </container>
- </containers>
- </unity>
- <startup>
- <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
- </startup>
- </configuration>
4. 引用接口
5.Main 方法如下:
- static void Main(string[] args)
- {
- var container = new UnityContainer();
- UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
- config.Configure(container, "LockA");
- ILock l = container.Resolve<ILock>();
- Console.WriteLine(l.Init(2));
- Console.WriteLine(l.Init(1));
- Console.WriteLine(l.Init(-1));
- Console.ReadKey();
- }
6. 复制门锁 A 的 dll 复制到运行目录
7. 运行结果如为: False,True,False, 是门锁 A 的结果
8. 将 "LockA" 改成 "LockB", 并将门锁 B 的 dll 复制到运行目录, 运行结果为 True,False,False 是门锁 B 的结果
算 DI 吧? 咱都用了 Unity 了.
至此, 基本实现了想要的功能, 未来新酒店要增加门锁 C, 门锁 D 都没问题, 只有老酒店改成门锁 E 的问题没解决(估计可以通过动态修改 config 文件实现, 也不知道有没有更好地办法).
来源: https://www.cnblogs.com/catzhou/p/9048966.html