重构的读书笔记, 简单转成python版本的code, 供参考

章8: 重新组织数据

8.1 Self Encapsulate Field 自封装字段

你直接访问一个字段, 但字段间的耦合关系逐渐变额笨拙. 为这个字段建立取值/设置函数, 并且只以这些函数来访问字段

间接访问的好处是, 可以灵活改变获取数据的途径

class A(object):
    def include(self, arg):
        return self.low < arg < self.high

to

class A(object):
    def include(self, arg):
        return self.low < arg < self.high

    @property
    def low(self):
        return self._low

    @property
    def height(self):
        return self._high

8.2 Replace Data Value with Object 以对象取代数据值

你有一个数据项, 需要与其他数据和行为一起使用才有意义

独立成对象

class Customer(object):
    def__init__(self, name, area_code, number):
        ....

    def get_phone_number(self):
        return "%s-%s" % (self.area_code, self.number)

to

class Phone(object):
    def __init__(self, area_code ,number):
        ....
    @property
    def number(self):
        return "%s-%s" % (self.area_code, self.number)


class Customer(object):
    def__init__(self, name, area_code, number):
        ....

8.3 Change Value to Reference 将值对象改为饮用对象

你从一个类衍生出许多彼此相等的实例, 希望将他们替换为同一个对象. 将这个值对象变成引用对象

# only 4 types, but 10000 A instance
class A(object):
    def __init__(self, type_id, type_name):
        self.type_id = type_id
        self.type_name = type_name

to

class Type(object):
    def __init__(type_id, type_name):
        self.type_id = type_id
        self.type_name = type_name

class A(object):
    def __init__(self, _type):
        self._type = _type

8.4 Change Reference to Value 将引用对象改为值对象

你有一个引用对象, 很小且值不可变, 而且不易管理. 将它变成一个值对象

手法, 将重构目标变成不可变对象. 即, 只有可以变成不可变对象的才能运用这个技巧

class Currency(object):
    def __init__(self, code):
        self.code = code

to

class Currency(object):
    def __init__(self, code):
        self._code = code

    @property
    def code(self):
        return self._code

    def __eq__(self, other):
        return self.code == other.code

8.5 Replace Array with Object 以对象取代数组

你有一个数组, 其中元素各自代表不同的东西. 以对象替换数组, 对于数组中的每个元素, 以一个字段来表示

row = ["Liverpoo", 15]

to

claas Record(object):
    def __init__(self, name, wins):
        self.name = name
        self.wins = wins

record = Record("Liverpoo", 15)

8.6 Duplicate Observed Data 复制"被监视数据"

你有一些领域数据置身于 GUI 控件中, 而领域函数需要访问这些数据. 将数据复制到一个领域对象中. 建立Observer模式, 用以同步领域对象和GUI对象内的重复数据

observer模式, 不解释

8.7 Change Unidirectional Association to Bidirectional 将单向关联改为双向关联

两个类都需要使用对方特性, 但其间只有一条单向连接. 添加一个反向指针, 并使修改函数能够同时更新到两条连接

class Customer(object):
    def get_orders(self):
        # need a lot of codes here

class Order(object):
    def __init__(self):
        pass

    @property
    def customer(self):
        return self._customer
    @customer.setter
    def customer(self, customer):
        self._customer = customer

    def get_customer_address():
        return self._customer.address

to

class Customer(object):
    def __init__(self):
        self.orders = []

    def add_order(self, order):
        self.orders.append(order)

    def get_orders(self):
        return self.orders

class Order(object):
    def __init__(self):
        pass

    @property
    def customer(self):
        return self._customer

    @customer.setter
    def customer(self, customer):
        self._customer = customer
        self._customer.add_order(self)

    def get_customer_address():
        return self._customer.address

8.8 Change Bidirectional Association to Unidirectional 将双向关联改为单向关联

两个类之间有双向关联, 但其中一个类如今不再需要另一个类的特性了

上面的例子, 如果Customer不再需要get_orders. 则可以去掉双向连接, 防止僵尸对象出现

8.9 Replace Magic Number with Symbolic Constant 以字面常量取代魔法数

你有一个字面数值, 带有特别含义. 创建一个常量, 根据其意义为它命名, 并将上述字面数值替换为这个常量

def potential_energy(mass, height):
    return mass * 9.81 * height

to

GRAVITATIONAL_CONSTANT = 9.81
def potential_energy(mass, height):
    return mass * GRAVITATIONAL_CONSTANT * height

8.10 Encapsulate Field 封装字段

你的类中存在一个public字段, 将它声明为private, 并提供相应访问函数

class A(object):
    def __init__(self, value):
        self.value = value

to

class A(object):
    def __init__(self, value):
        self.__value = value

    @property
    def value(self):
        return self.__value

    @value.setter
    def value(self, v):
        self.__value = v

8.11 Encapsulate Collection 封装集合

有个函数返回一个集合. 让这个函数返回该集合的一个只读副本, 并在这个类中提供添加/移除集合元素的函数

class A(object):
    def __init__(self):
        self._members = []
    def get_members(self):
        return self._members

to

class A(object):
    def __init__(self):
        self._members = []
    def get_members():
        return tuple(self._members)

    def add_member(self, member):
        self.members.append(member)

    def remove_member(self, member):
        self.members.remove(member)

8.12 Replace Record with Data Class 以数据类取代记录

你需要面对传统编程环境中的记录结构. 为该记录创建一个"哑"数据对象

可能面对的是一个遗留程序, 需要与其记录进行结构交流. 例如从数据库读出来的记录, 接口调用返回数据等

8.13 Replace Type Code with Class 以类取代类型码

类之中有一个类型码, 但它并不影响类的行为. 以一个新的类替换该数值类型码

class Person(object):
    def __init__(self):
        self.O = 0
        self.A = 1
        self.B = 2
        slef.AB = 3

to

class Person(object):
    def __init__(self):
        self.O = BloodGroup.O
        self.A = BloodGroup.A
        self.B = BloodGroup.B
        slef.AB = BloodGroup.AB

8.14 Replace Type Code with Subclasses 以子类取代类型码

你有一个不可变的类型码, 它会影响到类行为. 以子类取代类型码

使用多态来处理, 需要在一个类中使用if-else或者switch来根据类型码做出不同行为的类

class Employee(object):
    def __init__(self):
        self.ENGINEER = 1
        self.SALESMAN = 0

to

class Employee(object):
    def __init__(self):
class Engineer(Employee):
    pass
class Salesman(Employee):
    pass

8.15 Replace Type Code with State/Strategy 以Stat/Strategy取代类型码

你有一个类型码, 它会影响类的行为, 但你无法通过继承手法消除它. 以状态对象取代类型码

State模式和Strategy模式

8.16 Replace Subclass with Fields 以字段取代子类

你的各个子类的唯一差别只在"返回常量数据"的函数上. 修改这些函数, 使它们返回超类中的某个(新增)字段, 然后销毁子类

class Person(object):
    def is_male(self):
        pass
    def get_code(self):
        pass

class Male(Person):
    def is_male(self):
        return True
    def get_code(self):
        return "M"

class Feale(Person):
    def is_male(self):
        return False
    def get_code(self):
        return "F"

to

class Person(object):
    def is_male(self):
        return self._is_male
    def get_code(self):
        return 'M' if self._is_male else "F"