本章内容型别层次(is-a)进行重构原则分类:找出共通性、差异性,越接近根部,越通用,越接近底部,越具体。合併:分类后组成超型别(共通性)、子型别(差异性)。可替换性:里氏替换:超型别可以替换成子型别物件。路径:超型别=>子型别之间,不要产生没有意义的中间层。顺序 :依赖顺序原则=>子类别依赖超型别(O)、超型别依赖子型别(X)1.分类:使用IF-ELSE 而不使用物件导向的多型(6-1全)解决方式:使用多型行为一样的分层:只有当子类别有比基底类别更特殊的行为时,分类才有意义(6-2全)解决方式: 考虑使用Enum或组合6-2案例研究:JAVA基本型别不能支援泛型参数
以下是我的一个泛型类别:
public class Box<T> { private T value; public Box(T value) { this.value = value; } public T getValue() { return value; } public void setValue(T value) { this.value = value; }} // 其他的方法...
在Java当中,无法使用Box,需要使用Box,这将导致,当类别需要使用到int特性的方法时,需要用以下方式写:
public class IntBox { private int value; public IntBox(int value) { this.value = value; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } // 其他的方法...(int或Integer都可以处理的方法... + 你可能希望特定于int的方法...)}
这样写的坏处在于会产生重複的程式码(int或Integer都可以处理的方法...),并且如果我要换成实作Long的基本型别类别,又要再写重複的程式码。此时可以用组合的方式解决:
//用组合解决public class IntBox { // int或Integer都可以处理的方法 private Box<Integer> innerBox; public IntBox(int value) { this.innerBox = new Box<>(value); } public int getValue() { return innerBox.getValue(); } public void setValue(int value) { innerBox.setValue(value); } // 你可能希望特定 于int的方法...
如此一来,我只需要实作特定于int才能使用的方法就可以了,剩余的部分透过操作Box innerBox去处理。
2.合併:重複(6-3全):子类别有重複的地方,并没有移到超型别Java泛型问题重複的原因仅是因为定义上的差异解决方式:向上移动 删除子型别的方法注意:其他子型别是否通用 否则会造成叛逆过宽(6-4全):子型别过多,中间层不足解决方式:新增中间层,提取子型别重複向上-凭空想像解决方式:删除过度设计的中间层3.过深:子类别层数过高 原因是因为过度想重用解决方式:删除中间层应该也可以用策略模式/组合处理掉补充: marker interface
Marker Interface 是不包含任何方法的接口。它的主要目的是对某个类别提供一种元数据(metadata)的标记或标识。它告诉编译器或JVM该类具有某种特性或行为,但该接口本身不定义任何行为。
Serializable:
- 任何想要被序列化的类都应该实现Serializable接口。这个接口本身并不定义任何方法,但当一个类实现了它,JVM就知道它可以被序列化。
- 已经逐渐式微,目前透过注解+实作方法就可以了。
import java.io.Serializable; public class Person implements Serializable { private String name; private int age; // getters, setters, constructors, etc.}
Cloneable: 如果一个类想要使用Object的clone方法複製其对象,那么该类必须实现Cloneable接口。这是一个提示性的接口,它告诉JVM可以安全地执行某些複製操作(但还是需要自己实作複製方法,并不是实作了介面空方法就没事了)。
public class Book implements Cloneable { private String title; private String author; // Assume there are necessary methods and constructors @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }}
4.叛逆
拒绝超型别提供的方法 违反里氏替换看到一个类别有想用的方法,以及不想使用的方法,选择继承并把不想使用的方法拒绝(複写使其无用处)解决方式:定期重构 由超型别向下移移除方法组合/策略模式5.支离破碎
因为某些原因 乱用继承白玫瑰是一种玫瑰(O),玫瑰是一种白玫瑰(X)解决方式:组合 策略模式 继承关係颠倒转接器模式// 既有的印表机public class OldPrinter{ public void PrintPlainText(string text) { Console.WriteLine(text); }}// 目标接口public interface IRichTextPrinter{ void PrintRichText(string richText);}// 转接器public class PrinterAdapter : IRichTextPrinter{ private OldPrinter _oldPrinter; public PrinterAdapter(OldPrinter oldPrinter) { _oldPrinter = oldPrinter; } public void PrintRichText(string richText) { // 这边假设转换富文本为纯文字的处理,实际上可能会更複杂。 string plainText = ConvertRichTextToPlainText(richText); _oldPrinter.PrintPlainText(plainText); } private string ConvertRichTextToPlainText(string richText) { // 转换逻辑... return richText; // 假设返回值为纯文字格式 }}// 使用var oldPrinter = new OldPrinter();var adapter = new PrinterAdapter(oldPrinter);adapter.PrintRichText("<b>转接器</b>模式");
6.路径
未注意到既有的继承路径,导致子型别间接继承到中间层与超型别解决方式:删除继承超型别 或中间层7.循环
超类别包含子型别物件A拥有 B、B 拥有 C 、所以A拥有C、C是继承A的方法解决方式:状态模式 策略模式 职责区分小算盘:假设一开始不是用状态模式想的:我有一个小算盘其他不同情境下的小算盘继承我这个小算盘我的小算盘必须包含所有不同情境 去做操控