一个【五脏俱全】的类要交代八件事,15 行代码只做一件事;难的不是背关键字,是不知道该用哪几个。
1.4 列了三条原因,这一节讲第一条:语言里的构造到底多在哪。
一、一个最小的完整类
下面这个类只干一件事——把数字乘 2。但它把 OOP 的结构要素全占了。
15 行、5 个方法、3 个数据成员。
对比一下:过程式写这件事,一行 def double(n): return n * 2 就完了。
多出来的不是废话——每一行都在交代一件 C 语言里没有的事。
二、八件事分别是什么
一、类名;二、继承自谁(class Base(abc.ABC))。
三、类变量(count = 0,所有实例共享);四、构造(__init__)。
五、实例变量(self._n,下划线是约定私有);六、属性访问器(@property)。
七、抽象方法(@abc.abstractmethod,子类必须实现);八、覆写(子类重写 work)。
八件事少交代一件,类的语义就不完整。
三、各语言的构造数量
Java 要记 class、interface、extends、implements、abstract、final 等等——十来个关键字。
C++ 还有 public/private/protected/virtual/template——同样是十来个。
Python 没有这么多关键字,但有一堆双下划线约定(__init__、__str__、__eq__…),学习成本换了形式而已。
所以【换一门语言】并不能绕开这件事。
四、难在取舍,不在记忆
背下这些关键字,一天够了。难的是【这个需求该写几个类】。 同一个需求,写三个类能过,写八个类也能过——区别在三年后谁改得动。 这就是 1.1 说的:语法只是第一层,设计是第二层。 第 10 章会正面回答【该写几个类】——答案是拟人化和职责分配。
五、动手 Demo:原因之一:OOP结构复杂
一个最小但完整的类,把 OOP 的结构要素全用上,数一数它要交代几件事。 再列一列主流语言各自要你记住的类相关关键字。
import abc
class Base(abc.ABC): # 1 类名 2 继承自谁
count = 0 # 3 类变量
def __init__(self, n): # 4 构造
self._n = n # 5 实例变量
Base.count += 1
@property
def n(self): # 6 属性访问器
return self._n
@abc.abstractmethod
def work(self): ... # 7 抽象方法
class Impl(Base):
def work(self): # 8 覆写
return self.n * 2
print("方法数", len([1 for k in dir(Base) if not k.startswith('__')]))
print("结果 ", Impl(21).work())
实跑输出:
--------------------------------------------------------------------
原因之一——OOP 结构复杂
--------------------------------------------------------------------
一个【五脏俱全】的类,要交代八件事。逐条对上代码:
import abc
class Base(abc.ABC): # 1 类名 2 继承自谁
count = 0 # 3 类变量
def __init__(self, n): # 4 构造
self._n = n # 5 实例变量(下划线=约定私有)
Base.count += 1
@property
def n(self): # 6 属性访问器
return self._n
@abc.abstractmethod
def work(self): ... # 7 抽象方法(子类必须实现)
class Impl(Base):
def work(self): # 8 覆写
return self.n * 2
实测这份最小但完整的类:**15 行代码、5 个方法、3 个数据成员**。
而它只做一件事——把数字乘 2。
再看主流语言各自要你记住的【OOP 构造】有多少:
语言 类相关关键字 数量级
----------------------------------------------------------------
Java class interface extends implements 十来个
C++ class public private virtual template 十来个
Python class 加 __init__ 等双下划线约定 靠约定
JavaScript class extends super(早期靠原型) 少
**难点不在【背不下来】,在【不知道该用哪几个】。**
同一个需求,写三个类能过,写八个类也能过,**区别在三年后谁改得动。**
这就是 1.1 说的:语法只是第一层,设计是第二层。
实测:这个类 15 行,做的是【乘 2】——过程式一行就够。 多出来的每一行都在交代 C 里没有的概念:谁能看、谁继承谁、子类必须实现哪个。
30 秒实验
- 把你常用的一个类拿出来,标一标它用到了这八件事里的哪几件。
- 试着用过程式改写它,看会丢掉哪些约束。
常见误区
- 以为结构复杂是【语法丑】。复杂是因为要表达的东西多了。
- 以为 Python 简单就没这问题。关键字少了,约定多了。
自测三问(点开对答案)
1. 一个完整的类通常要交代哪八件事? —— 类名、继承自谁、类变量、构造、实例变量、属性访问器、抽象方法、覆写。 2. 为什么说难点不在背关键字? —— 背下来只要一天,难的是判断这个需求该写几个类、每个类该负责什么。 3. Python 的类相关关键字少,是不是就简单? —— 不是,成本换成了双下划线约定(__init__、__str__、__eq__ 等),一样要学。- ☐ 下一节(1.6 原因之二:滥用比喻引起混乱)
相关笔记
- 面向对象难的原因 — 三条原因的总览
- 原因之二:滥用比喻引起混乱 — 下一条
- ☕ Java基础 · 索引 — 这些关键字在 Java 里长什么样
- 🧩 面向对象是怎样工作的 · 索引