简化 写简单的代码,是每个程序猴最为重要的事情之一。 本章提出了几点建议: ## 组合方法 同一个方法中,它们的**细节应该保持一致**。这样将更容易理解。 把方法分解成命名良好的、处在细节的同一层面上的行为模块,以此来简化方法。 上层调用将不再关心细微的处理。而是业务逻辑。 例如: ``` def calc(type): if type==0: # 一大段非常多细节的代码 pass dosth() # 对细节的封装 # 重构后 def calc(type): if type==0: dosth1() else: dosth2() ``` ## 使用strategy替换条件逻辑 **把算法的每个变体搬移到新类或子类通常是更好的选择**。这实际上就是对象组合和继承之间的选择。 讲白了。就是抽象出算法的部分,建立新的类。然后将它组合进原来的类中。 值得注意的是。switch case通常不能被简单的消除,而只是换了一种形式。比如以下示例: ``` def capital(type): if type==1: pass if type==2: pass ... return result # 重构后 loan = new loan() loan.inner_capital = new Type1Capital() loan.capital() # 这里消除了type。因为依赖type的选择分支被拆分到了不同的Capital类中 ``` 但是,如何根据实际选择组合Type1Capital,还是Type2Capital 呢?恐怕,除了反射也没有很好的办法消除这一层逻辑了。 ## 将装饰功能搬移到Decorator 如果只是需要对原有的类进行边角功能的扩充,则可以考虑使用装饰器。 但是: > 如果需要搬移装饰功能的类包含许多的公共方法,那么Decorator模式不应该是重构的选择。 因为装饰器应该是透明的。即,被包装的对象不应该感知到它被包装。 在理想情况下,装饰器也不应该互相影响。否则,它们之间就会有顺序的依赖问题。 示例: 对一个Html的node在tostring的时候是否应该解码 ``` class StringNode(): def __init__(self): self.should_decode=False def get_str(self): ... if self.should_decode: return decode(result) return result 重构为: # 将decode(result)的逻辑抽离出StringNode类 string_node = new StringNode() decode_node = new DecodeNode(string_node) decode_node.get_str() ``` ## 用state替换状态改变条件语句 > 把状态改变条件逻辑从类中去除,并搬移到表示不同状态的一系列类中,可以产生更简单的设计。 如果把所有状态的迁移都放在一个地方处理(不使用状态模式)则很可能该方法的责任过大。方法体也会很长。 状态模式实质上,是把在一个类中不断的state变换,分解成一个个子状态。子状态类中,只关心状态的迁移。也就是上下文。 示例: ``` class Task(object): def __init__(self): self.state = 'nostart' def run(self): if self.state=='nostart': self.state = 'doing' elif self.state == 'doing': if self.check(): self.state = 'done' else: self.state = 'undone' self.update() def check(self): # ... return True def update(self): # ... pass task = Task() print(task.state) task.run() print(task.state) task.run() print(task.state) ``` 在这个例子中,所有状态的转化都依赖于主类的run。且,check操作只有在state==doing的时候才用的上。此时,可以进行重构。 重构后: ``` class Task(object): def __init__(self): self.state = NoStart() def run(self): self.state.run(self) self.update() def set_state(self, state): self.state = state def update(self): pass class State(object): def run(self, task): pass def get_str(self): pass class NoStart(State): def run(self, task): new_state = Doing() task.set_state(new_state) def get_str(self): return 'nostart' class Doing(State): def run(self, task): if self.check(): task.set_state(Done()) else: task.set_state(UnDone()) def get_str(self): return 'doing' def check(self): # ... return True class Done(State): def run(self, task): pass def get_str(self): return 'done' class UnDone(State): def run(self, task): pass def get_str(self): return 'undone' task = Task() print(task.state.get_str()) task.run() print(task.state.get_str()) task.run() print(task.state.get_str()) ``` 此时,在主task中,run被抽象为执行当前的state的run。这样做的好处在于: 1. 流程的控制下移到了state。此时在state中,只需要关系当前状态下的转移。而不需要再次对当前状态进行判断。`if self.state=='nostart':`这样的句子被移除。 2. 不通用的`check`函数从主类移除。 ## 用Composite替换隐含树 这个重构其实很常见,即,将隐含的树形结构用组合模式来表示。 实际上,系统提供的xml包就是一个很不错的例子。 ## 用Command替换条件调度程序 command模式,实质上是将执行的一行行命令原子化和抽象化。 比如: ``` if xxx: # dosth1 elif xxx: # dosth2 重构为 if xxx: task = Dosth1() task.exec(args) elif xxx: task = Dosth2() task.exec(args) ``` 将子任务抽象为相同的`exec`接口,会使得系统更为明晰。虽然上面的重构没有包含command_list。但是仍然是一种不错的思路。 当走出这一步,就会有意想不到的收获。 来自 大脸猫 写于 2018-03-13 16:17 -- 更新于2020-10-19 13:06 -- 3 条评论