Introduce Foreign Method一样,很多时候你不能修改编辑原始类,你需要为这些服务类增加一些额外的函数,但你没有这个权限或者入口。如果你只需要一个或者两个外加函数那么你可以放心的使用Introduce Foregin Method,但是如果你发现此时有很多外加函数需要在客户类代码中中添加,你就要小心了,因为你这么做你就会让客户类变得过分复杂,责任就会过分多,你会破坏客户类的单一职责性。这个时候你就可以建立一个新类,让他来包含这些你之前所添加的额外函数,让这个扩展类成为源类的子类或者包装类

在这里提供了两种标准对象技术--子类化(subclass)和包装(wrapping),在这两种方法下,作者统一称他们为本地扩展(Local Extension)。所谓本地扩展就是一个独立的类,但也是被扩展类的子类型,他可以提供源类的一切特性同时可以添加新特性,在任何使用源类的地方你都可以使用本地扩展取而代之。

坚持使用本地扩展可以让你坚守“函数和数据应该被统一封装”的原则。如果你一直把本该放在扩展类的代码零散地放置于其他类中,最终你会让这些客户类变得过分复杂,让其中的函数变得难以复用。

在子类和包装类之间做选择时,作者推荐优先使用子类,这也是有道理的,因为子类可以让你最少的去修改或者去添加源类本身有的特性,包装类你需要做一系列简单委托来维持原特性。但是子类化也是有比较麻烦的地方,就是它必须在对象创建期实施,如果你可以接管对象的创建期那当然没问题。但如果你想在对象创建之后再进行本地扩展那就有问题了。此外你还需要去面对别名问题,因为子类化对象通常是新创建一个子类对象,在这种情况下如果还有其他类引用了旧对象,你就同时有了两个对象保存了相同的原始数据。如果原数据是不可修改的,那还好没有什么问题,但如果是可修改的,你的问题就来了,因为一个修改不可能同时修改两个副本,如果你需要面对这个问题,那么你就应该使用包装类,因为只有使用包装你才可以让本地扩展(Local Extension)波及原对象,反之如果你不想让本地使用波及原来的数据,那么你应该优先使用子类化的方案。

  • 做法:
  • 建立一个扩展类,由你选择判断让他作为源类的子类或者包装类。
  • 在扩展类中加入“转型构造函数”,所谓“转型构造函数”就是指“接受原对象作为参数”的构造函数。如果采用子类化方案,那么转型构造函数应该调用适当的超类构造函数,如果采用包装类方案,那么转型构造函数应该将它得到的传入参数以实例变量的形式保存起来,用以接受委托对象。注意:使用转型构造函数更多是为了隐藏扩展类,让用户从使用上,特别是针对函数参数是源类的时候,可以做到转型构造。
  • 在扩展类中加入新特性。
  • 根据需要将原对象替换为扩展对象。
  • 将针对原始类增加的所有外加函数搬移到扩展类中。

例子:

class MfDateSub : public Date
{
public:
MfDateSub *nextDay() const...
int dayOfYear()...
}; class MfDateWrap
{
private:
Date *m_date;
};

针对我们上一篇的例子,我们有这两种方式来引入本地扩展,一个就是子类化,一个就是包装类。我们先来看看子类化

class MfDateSub : public Date
{
public:
MfDateSub(const QString &dateString) :
Date(dateString)
{
}
};

我们可以把基类构造函数的参数传入给基类的构造函数来完成扩展类的构造,现在我们需要同时引入一个转型构造函数,参数就是源类对象。

class MfDateSub : public Date
{
public:
MfDateSub(const QString &dateString) :
Date(dateString)
{
}
MfDateSub(Date *date) :
Date(date->getTime())
{
}
};

现在我可以在扩展类中增加新特性,用Move Method将之前添加的额外函数nextDate()移动到这个扩展类

clien class...
static Date *nextDay(Date *arg)
{
// foreign method
return new Date(arg->getYear(), arg->getMonth(), arg->getDate() + );
} class MfDateSub...
Date *nextDate()
{
return new Date(getYear(), getMonth(), getDate() + );
}

我们再来看下使用包装类的过程

class MfDateWrap
{
public:
MfDateWrap(const QString &dateString)
{
m_date = new Date(dateString);
}
private:
Date *m_date;
};

包装类的构造函数和之前有所不同,现在的构造函数只是很简单的执行一个委托动作。而转型构造函数则只是对它的字段赋值而已。

MfDateWrap(Date *arg)
{
m_date = arg;
}

接下来就是一连串枯燥的工作,我们要为原始类的所有函数提供委托函数。

int getYar()
{
return m_date->getYear();
} int getMonth()
{
return m_date->getMonth();
}

完成这个工作之后我们进行Move Method把额外函数引入到扩展类

class MfDateWrap...
Date *nextDate()
{
return new Date(getYear(), getMonth(), getDate() + );
}

当然,使用包装类有一个问题就是,就是如何处理接受原始类作为参数的函数,比如

bool after(Date *arg):

由于我们无法改变原始类,我们只能在一个方向上做到兼容,即在包装类的after()函数可以接受包装类或者原始类对象,但原始类的after()函数只能接收原始类对象,不能接受包装类对象。

wrapper->after(date); // ok
wrapper->after(anotherWrapper); // ok
date->after(wrapper); // error

所以其实我们应该显示的给包装类增加一个额外的after函数进行覆写(子类化不需要,因为子类指针可以被基类指针的参数所接受)即

bool after(Wrapper *arg):

这样覆写的目的就是为了向用户隐藏包装类的存在,这是一个好策略,因为包装类的客户的确不应该知道包装类的存在,的确应该可以同样的对待原始类和包装类,但是我们无法完全隐藏包装类的存在,原因就是上文所说的,源类可能不能通过包装类的参数来完成函数的调用,特别是对于equals()这种满足交换率的来说更是如此。因此我们只能做出妥协,创建一个新的函数来单独完成针对两种类的比较

bool equalsDate(Date *arg);
bool equalsDate(MfDateWrap *arg);

这样你就不必检查未知对象的类型了。当然了子类化方案中没有这样的问题,只要我不覆写原函数就行了。但如果你覆写了原始类中的函数,那么当你寻找函数时就容易晕头转向,一般来说,我们不会在扩展类中覆写原始函数,只会添加新函数。

『重构--改善既有代码的设计』读书笔记----Introduce Local Extension的更多相关文章

  1. 『重构--改善既有代码的设计』读书笔记----Introduce Foreign Method

    当你无法获得一个类的源代码或者没有权限去修改这个类的时候,你对于这种为你服务的类,你可能会出现需要别的需求的时候,比如一个Date类,你需要能够让他本身直接返回出他的后一天的对象,但他没有,这个时候你 ...

  2. 『重构--改善既有代码的设计』读书笔记----Introduce Explaning Variable

    有时候你会遇到一系列复杂的表达式连续运算的时候,这个时候你可能根本招架不住如此长或者是如此复杂的长函数.这个时候你可以通过引用临时变量来储存他们的结果,将这些长函数的结果分成一个个临时变量来让函数清晰 ...

  3. 『重构--改善既有代码的设计』读书笔记----Extract Method

    在编程中,比较忌讳的一件事情就是长函数.因为长函数代表了你这段代码不能很好的复用以及内部可能出现很多别的地方的重复代码,而且这段长函数内部的处理逻辑你也不能很好的看清楚.因此,今天重构第一个手法就是处 ...

  4. 『重构--改善既有代码的设计』读书笔记----Change Value to Reference

    有时候你会认为某个对象应该是去全局唯一的,这就是引用(Reference)的概念.它代表当你在某个地点对他进行修改之后,那么所有共享他的对象都应该在再次访问他的时候得到相应的修改.而不会像值对象(Va ...

  5. 『重构--改善既有代码的设计』读书笔记----Replace Method with Method Object

    有时候,当你遇到一个大型函数,里面的临时变量和参数多的让你觉得根本无法进行Extract Method.重构中也大力的推荐短小函数的好处,它所带来的解释性,复用性让你收益无穷.但如果你遇到上种情况,你 ...

  6. 『重构--改善既有代码的设计』读书笔记---Duplicate Observed Data

    当MVC出现的时候,极大的推动了Model与View分离的潮流.然而对于一些已存在的老系统或者没有维护好的系统,你都会看到当前存在大把的巨大类----将Model,View,Controller都写在 ...

  7. 『重构--改善既有代码的设计』读书笔记----Replace Array with Object

    如果你有一个数组,其中的元素各自代表不同东西,比如你有一个 QList<QString> strList; 其中strList[0]代表选手姓名,strList[1]代表选手家庭住址,很显 ...

  8. 『重构--改善既有代码的设计』读书笔记----Self Encapsulate Field

    如果你直接访问一个字段,你就会和这个字段直接的耦合关系变得笨拙.也就是说当这个字段权限更改,或者名称更改之后你的客户端代码都需要做相应的改变,此时你可以为这个字段建立设值和取值函数并且只以这些函数来访 ...

  9. 『重构--改善既有代码的设计』读书笔记----Move Method

    明确函数所在类的位置是很重要的.这样可以避免你的类与别的类有太多耦合.也会让你的类的内聚性变得更加牢固,让你的整个系统变得更加整洁.简单来说,如果在你的程序中,某个类的函数在使用的过程中,更多的是在和 ...

随机推荐

  1. 从 3 个 IT 公司里学到的 57 条经验

    自1999年起我就开始发掘一些科技公司,并帮助它们运营.下面是从干这行中得到的57条经验.我可以列出更多,但恐怕会令你厌烦. 1.做你个人有热情的事情.你是你自己最好的民意代表. 2.用户体验很重要. ...

  2. tyvj P1952 Easy(递推+期望)

    P1952 Easy 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 某一天WJMZBMR在打osu~~~但是他太弱逼了,有些地方完全靠运气:(我们来简化一下 ...

  3. springMVC能做什么,做j2ee时候要考虑什么

    转载: http://jinnianshilongnian.iteye.com/category/231099 [置顶] 跟我学SpringMVC目录汇总贴.PDF下载.源码下载 博客分类: 跟开涛学 ...

  4. Download SymmetricDS Data Sync Software for Free

    Download SymmetricDS Data Sync Software for Free Download SymmetricDS

  5. 323. Number of Connected Components in an Undirected Graph

    算连接的..那就是union find了 public class Solution { public int countComponents(int n, int[][] edges) { if(e ...

  6. Java中的一些常见错误

    1.空指针错误 在java数组的使用中,有时候需要对字符串数组中的元素进行对比.那么当元素不为null时,程序会正常运行:然而,一旦对比的元素为null,那么程序就会出现空指针错误. 解决方法:加入保 ...

  7. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(43)-工作流设计-字段分类设计

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(43)-工作流设计-字段分类设计 系列目录 建立好42节的表之后,每个字段英文表示都是有意义的说明.先建立 ...

  8. Open XML Format SDK引用

    Excel的便捷使得其在非开发人员的办公中非常流行,而Excel确实也提供了很多有用的功能.很多时候我们还需要以Excel为数据源来进行处理或者将Excel作为模板来生成一些报表.在Open XML ...

  9. 怎样在delphi中实现控件的拖拽

    下面这2种方法都能实现对控件和窗体的拖拽 方法1 procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift ...

  10. android 10 事件

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layo ...