《Effective Ruby:改善Ruby程序的48条建议》一第14条:通过protected方法共享私有状态

第14条:通过protected方法共享私有状态

封装是面向对象编程中的主要准则之一,它是指一个对象的内部实现仅可被内部访问,不可被外部访问。这使得我们可以构建一层壁垒。一方面,暴露那些允许别人使用的外部接口。另一方面,可以灵活地改变其内部的实现而无需为破坏类的外部行为而担忧。在Ruby语言中,这种壁垒可能理论味十足,但至少是存在的。
以实例变量为例——它们默认是私有的。如果不借助任何后门式的元编程技巧,它们只能在实例方法内部被访问。若将其暴露到外部,则需定义被称为accessor的方法。这是个好消息。你可以放心地在实例变量中存储内部状态而无需担心它可能会意外地成为公共接口的一部分。这样的封装在多数情况下都是极好的,但有一种情况它们反而会成为阻碍。因为某些时候一个对象需要访问另一个对象的内部状态。考虑下面的
例子:

方法Widget#overlapping?会检查其本身是否和另一个对象在屏幕上重合。Widget类的公共接口并没有将屏幕坐标对外暴露;它们的具体实现隐藏于内部。看上去突破封装的壁垒的唯一方法就是使用一些元编程技巧了。但这种方法有其自身的缺点。
通常情况下,当设计一个具有内部状态的类时,正确的做法是尽可能减少对该状态的直接访问,而是使用最小数量的访问方法。这样做既提升了可测试性也降低了变更可能对系统造成的破坏。把对于状态的操作隐藏于私有方法中也有助于优化程序,如优化内存(参考第48条)。为了提升可维护性,更好的做法是让Widget类的屏幕坐标信息保持私有,即便是对于overlapping?方法也应如此。你可能希望去写一个私有方法来充当访问屏幕坐标的接口,但在这种情况下并不适用。
在Ruby语言中,私有方法的行为和其他面向对象的编程语言中不太相同。Ruby语言仅仅在私有方法上加了一条限制——它们不能被显式接收者调用。无论你在继承体系中的哪一级。只要你没有使用接收者,你都可以调用祖先中的私有方法。但是你不能调用另一个对象的私有方法,即便是调用者和接收者隶属于同一个类。如果你使用一个私有方法来访问屏幕坐标,你仍然无法在overlapping?方法中使用它们。还好,Ruby提供了针对这种情况的解决方式:protected方法。
接收者无法使用私有方法,但可以使用protected方法。但有一个例外,而且它还有些复杂。被接收者调用的方法必须处于调用者继承体系中的哪一级,只要调用者和接收者都可以通过继承体系共享同一个实例方法,那么调用者就可以调用接收者上的方法。调用者和接收者并不需要是同一个类的实例,但是它们必须共享一个定义了该方法的超类。
对于Widget类来说,可以定义一个暴露私有屏幕坐标的方法,但并不通过公共接口来实现。其实现方式是声明该方法为protected,这样我们既保持了原有的封装性,也使得overlapping?方法可以访问其自身以及其他传入的widget实例的坐标。这样就无需元编程的黑魔法了!

这正是设计protected方法的原因——在相关类之间共享私有信息。
要点回顾
通过protected方法共享私有状态。
一个对象的protected方法若要被显式接收者调用,除非该对象与接收者是同类对象或其具有相同的定义该protected方法的超类。

时间: 2024-10-18 15:21:28

《Effective Ruby:改善Ruby程序的48条建议》一第14条:通过protected方法共享私有状态的相关文章

编写高质量代码改善C#程序的157个建议[IEnumerable<T>和IQueryable<T>、LINQ避免迭代、LINQ替代迭代]

原文:编写高质量代码改善C#程序的157个建议[IEnumerable<T>和IQueryable<T>.LINQ避免迭代.LINQ替代迭代] 前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议29.区别LINQ查询中的IEnumerable<T>和IQueryable<T> 建议30.使用LINQ取代集合中的比较器和迭代器 建议31.在LINQ查询中避免不必要的迭代

编写高质量代码改善C#程序的157个建议[为泛型指定初始值、使用委托声明、使用Lambda替代方法和匿名方法]

原文:编写高质量代码改善C#程序的157个建议[为泛型指定初始值.使用委托声明.使用Lambda替代方法和匿名方法] 前言 泛型并不是C#语言一开始就带有的特性,而是在FCL2.0之后实现的新功能.基于泛型,我们得以将类型参数化,以便更大范围地进行代码复用.同时,它减少了泛型类及泛型方法中的转型,确保了类型安全.委托本身是一种引用类型,它保存的也是托管堆中对象的引用,只不过这个引用比较特殊,它是对方法的引用.事件本身也是委托,它是委托组,C#中提供了关键字event来对事件进行特别区分.一旦我们

编写高质量代码改善C#程序的157个建议[用抛异常替代返回错误、不要在不恰当的场合下引发异常、重新引发异常时使用inner Exception]

原文:编写高质量代码改善C#程序的157个建议[用抛异常替代返回错误.不要在不恰当的场合下引发异常.重新引发异常时使用inner Exception] 前言 自从.NET出现后,关于CLR异常机制的讨论就几乎从未停止过.迄今为止,CLR异常机制让人关注最多的一点就是"效率"问题.其实,这里存在认识上的误区,因为正常控制流程下的代码运行并不会出现问题,只有引发异常时才会带来效率问题.基于这一点,很多开发者已经达成共识:不应将异常机制用于正常控制流中.达成的另一个共识是:CLR异常机制带来

编写高质量代码改善C#程序的157个建议[4-9]

原文:编写高质量代码改善C#程序的157个建议[4-9] 前言 本文首先亦同步到http://www.cnblogs.com/aehyok/p/3624579.html.本文主要来学习记录一下内容: 建议4.TryParse比Parse好 建议5.使用int?来确保值类型也可以为null 建议6.区别readonly和const的使用方法 建议7.将0值设为枚举的默认值 建议8.避免给枚举类型的元素提供显式的值 建议9.习惯重载运算符 建议4.TryParse比Parse好 如果注意观察,除st

编写高质量代码改善C#程序的157个建议[C#闭包的陷阱、委托、事件、事件模型]

原文:编写高质量代码改善C#程序的157个建议[C#闭包的陷阱.委托.事件.事件模型] 前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议38.小心闭包中的陷阱 建议39.了解委托的实质 建议40.使用event关键字对委托施加保护 建议41.实现标准的事件模型 建议38.小心闭包中的陷阱 首先我们先来看一段代码: class Program { static void Main(string[] arg

编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

原文:编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串.实现浅拷贝和深拷贝.用dynamic来优化反射] 前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深拷贝 建议15.使用dynamic来简化反射实现 建议13.为类型输出格式化字符串   有两种方法可以为类型提供格式化的字符串输出. 一种是意识到类型会产生格式化字符串输出,于是

编写高质量代码改善C#程序的157个建议[匿名类型、Lambda、延迟求值和主动求值]

原文:编写高质量代码改善C#程序的157个建议[匿名类型.Lambda.延迟求值和主动求值] 前言 从.NET3.0开始,C#开始一直支持一个新特性:匿名类型.匿名类型由var.赋值运算符和一个非空初始值(或以new开头的初始化项)组成.匿名类型有如下基本特性: 1.既支持简单类型也支持复杂类型.简单类型必须是一个非空初始值,复杂类型则是一个以new开头的初始化项. 2.匿名类型的属性是只读的,没有属性设置器,它一旦倍初始化就不可更改. 3.如果两个匿名类型的属性值相同,那么就任务这两个匿名类型

编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]

原文:编写高质量代码改善C#程序的157个建议[动态数组.循环遍历.对象集合初始化] 前言   软件开发过程中,不可避免会用到集合,C#中的集合表现为数组和若干集合类.不管是数组还是集合类,它们都有各自的优缺点.如何使用好集合是我们在开发过程中必须掌握的技巧.不要小看这些技巧,一旦在开发中使用了错误的集合或针对集合的方法,应用程序将会背离你的预想而运行. 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议16.

编写高质量代码改善C#程序的157个建议[10-12]

原文:编写高质量代码改善C#程序的157个建议[10-12] 前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议10.创建对象时需要考虑是否实现比较器 建议11.区别对待==和Equals 建议12.重写Equals时也要重写GetHashCode 建议10.创建对象时需要考虑是否实现比较器 有对象的地方就会存在比较,就像小时候每次拿着考卷回家,妈妈都会问你隔壁的那谁谁谁考了多少分呀.下面我们也来举个简单