[Design Pattern] Substitute Interface

目的

将对象的成员建立为替身接口的成员,用来解耦对象之间的循环相依。

情景

假设开发人员接手一个系统,在系统里有订单对象、送货物件,其中订单对象使用送货物件所提供的送货方法。

当系统继续设计下去,会发现送货方法需要订单对象的相关信息才能决定如何送货。例如说:送货方法需要商品数量,来决定要派卡车送货还是要派机车送货;送货方法需要商品价格来决定要用盒子包还是要用纸袋包。当送货方法的逻辑越来越复杂的时候,最终就会发现送货方法需要订单对象的所有成员才能满足送货方法的运算需求。

送货方法需要订单对象的所有成员,很直觉的设计就是将订单对象作为送货方法的参数,用以提供订单对象的所有成员来满足送货方法的运算需求。系统设计到了这个阶段,就会发现订单对象使用送货物件、而送货物件也使用了订单对象,这也就是产生了对象之间循环相依的问题。(在.NET中,对象之间的循环相依是能够通过编译、并且执行的设计)

为了解决对象之间循环相依的问题,回过头思考订单对象、送货物件之间的关系。会发现在送货物件的送货方法中,其实需要的是订单对象的「所有成员」,而不是真的需要一个订单对象。

这时开发人员可以将订单对象的所有成员,建立为一个「替身接口」并且让订单对象来继承这个接口,用来透过替身接口来提供订单对象的所有成员。接着送货物件中的送货方法,改为使用这个替身接口做为参数,这样就可以取得订单对象的所有成员,而不需要将订单对象作为送货方法的参数。也就是说经由这样的设计,让订单对象、送货物件都改为相依于替身接口,进而就解除了订单对象、送货物件之间循环相依的问题。

结构

参与者

Substitute

  • 提供Primary对象的所有成员。

Primary

  • 实作Substitute界面。
  • 持有Secondary对象的参考。
  • 将本身视为Substitute接口来做为Secondary对象的函式参数,用以提供所有成员。

Secondary

  • 使用Substitute接口做为函式参数,来取得Primary对象的所有成员。

合作方式

实作

  • 类别图

  • IOrder

    public interface IOrder
    {
    // Properties
    int Quantity { get; set; } int Amount { get; set; } // Methods
    void Deliver();
    }
  • Order

    public class Order : IOrder
    {
    // Fields
    private readonly Deliverer _deliverer = new Deliverer(); // Properties
    public int Quantity{get; set;} public int Amount { get; set; } // Methods
    public void Deliver()
    {
    _deliverer.Deliver(this);
    }
    }
  • Deliverer

    public class Deliverer
    {
    // Methods
    public void Deliver(IOrder order)
    {
    // Print
    Console.WriteLine(order.Quantity);
    Console.WriteLine(order.Amount);
    }
    }
  • Program

    class Program
    {
    static void Main(string[] args)
    {
    // Test
    var order = new Order();
    order.Quantity = 12345;
    order.Amount = 67890;
    order.Deliver(); // End
    Console.WriteLine("End...");
    Console.ReadLine();
    }
    }
  • 执行结果

下载

范例程序代码:点此下载

[Design Pattern] Substitute Interface的更多相关文章

  1. [转]Design Pattern Interview Questions - Part 4

    Bridge Pattern, Composite Pattern, Decorator Pattern, Facade Pattern, COR Pattern, Proxy Pattern, te ...

  2. [转]Design Pattern Interview Questions - Part 2

    Interpeter , Iterator , Mediator , Memento and Observer design patterns. (I) what is Interpreter pat ...

  3. [转]Design Pattern Interview Questions - Part 3

    State, Stratergy, Visitor Adapter and fly weight design pattern from interview perspective. (I) Can ...

  4. [转]Design Pattern Interview Questions - Part 1

    Factory, Abstract factory, prototype pattern (B) What are design patterns? (A) Can you explain facto ...

  5. 为什么要提倡“Design Pattern呢

    为什么要提倡“Design Pattern呢?根本原因是为了代码复用,增加可维护性. 那么怎么才能实现代码复用呢?面向对象有几个原则:开闭原则(Open Closed Principle,OCP).里 ...

  6. design pattern及其使用

    什么是设计模式? design pattern是一个通用的,可以被重用的关于一个常见的问题的解决方案. 为什么要用设计模式? 引入设计模式的理论基础非常简单.我们每天都会碰到问题.我们可能碰到决定使用 ...

  7. Asp.Net Design Pattern Studynotes -- Part1

    Asp.Net Design Pattern Studynotes -- Part1 let's start with an exampleto entry amazing OO world ! le ...

  8. [Design Pattern] Service Locator Pattern 简单案例

    Service Locator Pattern,即服务定位模式,用于定位不同的服务.考虑到 InitialContext::lookup 的成本比较高,提供了 Cache 类缓存以定位到的服务. 代码 ...

  9. [Design Pattern] Factory Pattern 简单案例

    Factory Pattern , 即工厂模式,用于创建对象的场景,属于创建类的设计模式 . 下面是一个工厂模式案例. Shape 作为接口, Circle, Square, Rectangle 作为 ...

随机推荐

  1. MyBatis+MySQL 返回插入的主键ID

    需求:使用MyBatis往MySQL数据库中插入一条记录后,需要返回该条记录的自增主键值. 方法:在mapper中指定keyProperty属性,示例如下: <insert id="i ...

  2. Android之线程回掉更新ui

    一:工作线程中的回掉更新UI public class MainActivity extends AppCompatActivity { private int i; private Callback ...

  3. js apply/call/caller/callee/bind使用方法与区别分析

    一.call 方法 调用一个对象的一个方法,以另一个对象替换当前对象(其实就是更改对象的内部指针,即改变对象的this指向的内容). Js代码 call([thisObj[,arg1[, arg2[, ...

  4. 调皮的MySQL服务 请你不要再躲猫猫

    问题说明 Win10企业版 64位: MySQL5.6 64位: 临时在本机部署一套系统,需要用到MySQL,发现Windows服务下居然找不到MySQL这个服务,原先配置好的GUI以及应用程序都连不 ...

  5. [Git] 快速签出与更新所有远程分支.md

    git-fetch 命令从远程仓库复制 heads 和 tags 信息到本地,保存在临时文件 .git/FETCH_HEAD 中以备 git-merge 命令使用. 你可以使用 git fetch 命 ...

  6. C中调用Lua函数

    我们先来看一个简单的例子: lua_State* L = NULL; // 内部调用lua函数 double f(double x, double y) { double z; lua_getglob ...

  7. Web程序员开发App系列 - 调试Android和IOS手机代码(补图)

    Web程序员开发App系列 Web程序员开发App系列 - 认识HBuilder Web程序员开发App系列 - 申请苹果开发者账号 Web程序员开发App系列 - 调试Android和iOS手机代码 ...

  8. 关于VSTO调用Excel后进程无法退出的解决方案:

    VSTO的Excel对象模型提供了托管代码对Excel的操作.但是它的实现时通过RCW(Runtime Com Wrapper)实现的,所以无法完全按照托管代码的运行方式操作.COM的资源释放时通过引 ...

  9. JavaScript 正则表达式提取感兴趣的字符串

    var tdid="gov_sslim"; var reg=/(\w+)lim/; var name=tdid.match(reg); console.log(name[1]); ...

  10. Android 开发有用代码积累

    Android开发需求变化快,开发周期要求尽量短,接下来一系列文章从实际使用出发总结一些常用的代码片段,便于查找,也为后来人提供一份参考. 1.获取Manifest的基本信息(升级页面和软件关于页面一 ...