类和接口

13,使类和成员的可访问性最小化

要设计良好的模块与设计不好的模块,最重要的因素在于,这个模块对外部的其他模块而言,是否隐藏内部数据和其他实现细节。设计良好的模块会把实现细节所有实现细节,把它的API与实现清晰的隔离开来。然后,各模块只通过它们的API进行通信,一个模块不需要知道其他模块的的内部工作情况,这个概念被称为信息隐藏封装,是软件设计的基本原则之一。

信息隐藏可以有效的解除组成系统各模块之间的耦合关系,使得这些模块可以独立地开发,测试,优化,使用,理解和修改。
提高了软件的可重用性,降低了构建大型项目的风险,因为模块之间联系不紧密。

对于顶层的类和接口,只有两种可能的访问级别:包私有的(package-private)和共有的(public)。

通过把或者接口做成私有,它实际上就成了包实现的一部分,而不该是API的一部分,以后可以对它进行修改替换。

如果把它做成共有的,你就有责任永远支持它,以保持它的兼容性。

如果一个包级私有的顶层类(接口)只是在某一个类的内部被用到,就应该考虑使它成为唯一使用它的那个类的私有类

  • 实例域决不能是共有的,包含共有域的类并不是线程安全的。
  • 同样的建议也适用于静态域,只有一种情况例外。假设常量构成了类提供的整个抽象中的一部分。可以通过静态final域来暴露这些常量。

如果final域包含对象的引用,它便拥有非final域的所有缺点。虽然引用本身不会变,但引用的对象却可以修改。

除了共有静态域的特殊情况外,共有类都不应该包含公有域。并且要确保静态final公有域所引用的对象时不可变的。

14,在公有类中使用访问方法而非公有域。

使用getter,setter来代替公有域访问。

  • 如果类可以在它的所在的包外部进行访问,就提供访问方法。
  • 如果类是包的私有级的,或私有嵌套类,直接暴露它的数据域并没有什么过错。
15,使可变性最小化

不可变类只是其实例不能被修改的类,如String。

理由:

不可变的类比可变的类更加易于设计,实现和使用。它们不容易出错,更安全。

遵循的规则:
  • 不要提供任何会修改对象状态的方法。
  • 保证类不会被扩展。一般使这个类称为final的。
  • 使所有的域都是final的。在缺乏同步机制情况下,实例从一个线程被传到另一个线程,必须确保正确行为。
  • 使所有的域都称为私有的。
  • 确保对任何可变组件的互斥访问。如果类具有指向可变对象的域,则必须保证客户端无法获得指向这些对象的引用。并且,永远不要用客户端提供的对象来初始化这样的域,也不要从任何访问方法中返回该对象的引用。

不可变对象只有一种状态,即被创建时的状态。使用函数的做法返回一个新的实例,而不是修改这个实例。

对于频繁用得到的值,为他们提供共有的静态final常量。

有关序列化,如果选择让自己的不可变类实现Serializable接口,并且它包含一个或者多个指向可变对象的域,就必须提供一个显式的readObject或者readResolve方法,或者使用ObjectOutputStream.writeUnshred和ObjectOutputStream.readUnshred,即使默认的序列化形式可以接受,也是如此。

16,复合优先于继承

与方法调用不同,继承打破了封装性。换句话说,子类依赖于其超类中特定功能的实现细节。超类的实现可能会变化,这时子类可能遭到破坏。

因而,子类必须要跟着超类更新而演变,除非超类是专门为了扩展而设计的,并且具有很好的文档说明。

导致子类脆弱的一个相关原因是,它们的超类在后续的版本中可以获得新的方法。

为了解决上面这个问题,不用扩展现有的类,而是在新的类中增加一个私有域,它引用现有类的一个实例。这种设计叫做复合,因为现有的类变成了新类的一个组件。新类中的每个实例都可以调用被包含的现有类实例中对应的方法,并返回它的结果。这被称为转发,新类中的方法被称为转发方法

这样得到的类将会非常稳固,它不依赖于现有的实现细节。即使现有的类添加了新的方法,也不会影响新的类。装饰纸模式就是这样实现的,对现有的类进行装饰,增加了功能。

注意:

包装类几乎没有任何缺点,但它不适合用在回调框架(CallBack FrameWork)中;在回调框架中,对象把自身的引用传给其他对象,用于后续的回调,但被包装的类不知道它的外部包装类。

当只有子类真正是超类的子类型时,才适用继承。对于两个类A和B,只有当两者之间确实存在"is a"关系的时候,类B才扩展类A。如果不能确定每个B确实也是A,那么就不应该扩展。

复合通常情况下,B应该只包含A的一个实例,并且只暴露一个较小的,较简单API:A本质上不是B一部分,只是它的实现细节而已。

在使用继承而不是复合之前,问自己最后一组问题:
  • 对于你正在扩展的类,它的API有没有缺陷
  • ?如果有,你是否愿意把这些缺陷传到类的API中?
  • 继承机制会把超类的所有缺陷都传到子类中,而复合允许设计新的API来隐藏这些缺陷。
17,要么为继承而生,并提供详细的文档,要么就禁止继承
  • 首先,该文档必须精确地描述覆盖每个方法所带来的影响。

对于每个共有的方法或者构造器,必须指明调用了哪些可覆盖的方法,是以什么顺序调用的,每个调用结果又是如何影响后续的处理过程。更一般,类必须说明,在哪些情况下回调用可覆盖的方法。

  • 为了使程序员能够编写出更加有效的子类,无需承受不必要的痛苦,类必须通过某种形式提供适当的钩子,以便能够进入到它的内部工作流程中,这种形式可以是精心选择的受保护的方法,也可以是受保护的域。
  • 构造器决不能调用可被覆盖的方法,无论是直接调用还是间接调用。
  • 对于普通的类,它们不是为了子类化而设计和编写文档的。可以禁止子类化:把类声明为final;把构造器变成私有的;或包级私有的,并增加静态工厂来替代构造器;或确保这个类永远不会调用它的任何可覆盖的方法,并在文档中说明。

必须在发布前先编写子类对类进行测试

经验表明,3个子类通常就可以测试一个可扩展的类。

18,接口优于抽象类
  • 现有的类可以很容易被更新,以实现新的接口。
  • 接口是定义mixin(混合类型的理想选择)。

mixin是指这样的类型:类除了它的基本类型之外(primary type),还可以实现这个mixin类型,以表明它提供了某些可供选择的行为。例如:Comprarable表明它的实例可以与其他的可比较的对象进行排序。

  • 接口允许我们构造非层次结构的类型框架。

接口使得安全地增强类类的功能成为可能。通过对你导出的每个重要接口都提供一个抽象的骨架实现类,把接口和抽像类的有点结合起来。接口任然是定义类型,但是骨架类接管理所有与接口实现相关的工作。(骨架实现类就是抽象类,但抽象方法是由接口确定的基本方法

19,接口只用于定义类型

不要使用常量接口模式。如果要导出常量用枚举类型添加到这个类或者接口中;或使用不可实例化工具类来导出这些常量。

接口应该只被用来定义类型,不应该用来导出常量。

20,类层次优先于标签类

例如,一个类通过不同构造参数创建圆和方形,可以计算面积。这时可以将它子类化:将共有的面积等抽象到一个类,子类圆和方形分别继承这个类。

多个程序员可以独立地扩展层次结构,并且可以不访问根类源代码就能相互操作。每种类型都有一种相关的独立的数据类型,允许程序员指明变量的类型,限制变量,并将参数输入到特殊的类型。

反映类型之间本质上的层次关系,有助于增强灵活性,并进行更好的编译时类型检查。

21,用函数对象表示策略
  • 简而言之,函数指针的主要用途就是策略模式。
  • 为了在java中实现这种模式,要声明一个接口来表示策略,并且为每个具体的策略声明一个实现了该接口的类。
  • 当一个具体策略只被使用一次,通常使用匿名类来声明和实例化这个具体策略类。
  • 当具体策略时设计来重复使用的时候,它的类通常就要被实现为私有的静态成员类,用通过共有的静态final域被导出,其类型为该策略接口。
22,优先考虑静态成员类(讨论嵌套类)

嵌套类是指被指定义在另一个类的内部的类。存在目的应该只是为了它的外围类提供服务。

如果嵌套类将来可能会用于其它的某个环境中,它就应该是顶级类。

嵌套类有四种:静态成员类(static member class);非静态成员类(nonstatic member calss);匿名类(anonymous class);局部类(local class)

除了第一种外,其它三种都被称为内部类(inner class)。

静态成员类:

最好把它当普通的类,只是碰巧被声明在另一个类的内部而已,它可以访问外围类的成员变量,包括哪些私有的成员。静态成员类是外围类的一个静态成员,与其他的静态成员一样,也遵循同样的可访问规则,如果私有只能在内部被访问。

静态成员类的一种常见用法是作为公有的辅助类,仅当它与外部类一起使用时才有意义。

静态成员类可以在它外围实例之外独立存在,而非静态成员类在没有外围实例的情况下不可能创建出实例对象。

  • 如果声明成员类不要去访问外围实例,就要始终把static修饰符放在它的声明中,使他成为静态成员类。如果省略了static修饰符,则每个实例都将包含一个额外的指向外围对象的引用。保存这份引用要消耗时间和空间,并且会导致外围实例在符合垃圾回收时仍然得以保留。
  • 如果在没有外围实例的情况下,也需要分配实例,就不能用非静态成员类,因为非静态成员类的实例必须要有一个外围实例。
私有静态成员类:

用来代表外围类所代表的对象的组件。

与静态成员类不同,每个外围对象都有一个私有静态成员类关联;与非静态成员变量不同的是,它并不需要访问外围类实例。

匿名类:

匿名类没有名字,它不是外围类的一个成员。它并不与其他成员以前被声明,而是在使用时被声明和实例化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 1.使类和成员的可访问性最小化 访问修饰符: private protected public 顶层的(非嵌套)类...
    666真666阅读 830评论 0 1
  • 类和接口 一、使类和成员的可访问性最小化 首先我们要了解一个 软件设计基本原则:封装 模块隐藏所有的实现细节,只通...
    dooze阅读 469评论 0 0
  • 第十七条、要么为继承而设计,并提供文档说明,要么就禁止继承 该类的文档必须精确地描述覆盖每个方法所带来的影响,即说...
    Timorous阅读 250评论 0 0
  • 第4章 类和接口 类和接口是Java程序设计语言的核心,它们也是Java语言的基本抽象单元。 初学Java的时候感...
    程序亦非猿阅读 831评论 4 13
  • 第十三条、使类和成员的可访问性最小化 设计良好的模块会隐藏所有的实现细节,把它的API和它的实现清晰地隔离开来。然...
    Timorous阅读 293评论 0 0