资讯动态

Python面向对象编程实战指南:从设计原则到项目落地

发布时间:2026/9/9 1:21:10 来源:尧图企业网站定制
做了无数个业务项目、代码量从几百行涨到几万行之后我深刻理解了一件事大部分人并不是不会写 class而是不知道在什么场景下该用 class以及该用什么级别的 OOP 手段去解决问题。Python 这门语言最大的迷惑性在于它几乎不逼你使用任何范式。你可以从头到尾写脚本式函数也可以像 Java 一样把一切都包进类里两者都能跑但上线后维护起来的差别往往能在三个月内拉开一个团队的执行力差距。针对“Python 面向对象编程OOP终极指南”这个命题我希望呈现的不只是语法速查而是从需求推导设计、从设计落代码、从代码反推坑点的完整链路。无论你是刚学完 Python 语法、准备跳槽面试的新人还是已经写了半年业务脚本但始终觉得自己没“入门”的工程师读完这篇文章后你应该能亲手设计出结构清晰、易扩展、经得起需求变更的类模型也能看懂别人代码里那些隐晦的继承体系和魔术方法到底在做什么。1. 设计层面的思考先从三个真实需求说起1.1 为什么 Python 里的 OOP 和 Java/C 不一样很多朋友第一次接触 OOP 是在 Java 课上于是天然以为面向对象就一定要有 private 关键字、要写 getter/setter、要 class 套 interface 再套实现类。落到 Python 里这种思维往往会产生一堆比脚本还难维护的“胖类”。Python 的 OOP 其实是“鸭子类型 动态属性 协议约定”的组合体。它不强求你隐藏内部状态也不强制你定义接口它更希望你遵循约定用下划线表示“别碰我”用魔术方法来融入语言本身。所以Python 里的设计原则不是靠编译器来兜底而是靠团队里的每个人理解“命名、边界、职责”这三个词。我见过一个项目早期赶进度所有函数都铺在模块里后来加了支付逻辑开始出现pay_by_alipay()、pay_by_wechat()、pay_by_card()这种复制粘贴式函数。这时候如果还不引入一个Payment类来统一接口下一次活动需求来的时候改 bug 的人一定会崩溃。1.2 面向对象到底解决了什么问题面向对象不是银弹它只是三种核心能力的组合封装把数据和对数据的操作绑在一起外部只看到有限的入口改内部实现不影响外部调用。继承把公共逻辑抽出来子类复用父类同时允许扩展差异。多态同一套调用代码传入不同的对象产生不同的行为调用方完全不用关心具体类型。在 Python 中这三个能力都可以用很轻量的方式实现。封装用命名约定和property继承用普通 class 即可多态甚至不需要继承只要对象有同名方法就行。这正是 Python OOP 的智慧所在约束越少越考验设计者的自律。1.3 什么代码适合用 OOP什么不适合我经常被问到到底什么时候该定义类什么时候直接写函数就行我的判断标准很简单如果只有一组独立函数数据以参数传入、结果以返回值传出那么函数式或脚本式写法更直接。如果存在需要长期维护的内部状态或者多个函数都要操作同一份数据或者未来有明确的“多套实现”切换需求这时类就很合适。如果你的代码里出现了同一个对象被反复塞给不同函数当第一个参数请立刻停下来把这个对象定义成类。举个例子爬虫项目里一个SpiderSession类维护请求头、代理池、限速策略比在 20 个函数里反复传session_params要优雅得多。2. 类与实例的核心设计——别再被 class 吓退2.1 类的诞生定义、实例化、初始化Python 使用class关键字定义类用类名()创建实例。很多初学者容易混淆“初始化”和“构造”。其实__init__并不负责创建对象它只负责在对象创建完成后填入初始数据。Python 真正创建对象是__new__在做但绝大多数场景不需要动它。来看一个实际业务里常见的用户类class User: def __init__(self, user_id: int, name: str, email: str): self.user_id user_id self.name name self.email email self._login_count 0 def login(self): self._login_count 1 print(f{self.name} 登录成功累计登录 {self._login_count} 次) def to_dict(self): return {user_id: self.user_id, name: self.name, email: self.email}这里_login_count以下划线开头代表“内部状态外部不要直接修改”。它不是绝对私有只是约定。Python 的态度是大家都是成年人靠自觉。2.2 self 到底是什么self在所有实例方法中都是第一个参数它指向调用这个方法的实例本身。很多人问为什么 Python 不能像 Java 一样隐式帮你传this原因很简单显式比起隐式更直白Python 之禅说的。你把self写成任何名字都能运行但强烈不建议因为全社区都认self。理解self对调试非常关键。当你看到一个实例方法报错说missing 1 required positional argument: self通常不是没传 self而是你把实例方法当普通函数调用了。比如user.login这种写法拿到的只是一个绑定方法对象并没有真正执行。要执行必须加括号user.login()。2.3 类属性、实例属性、查找顺序类属性是定义在class体内的变量被所有实例共享。实例属性是在__init__或其他实例方法里通过self.xxx创建的属于单个实例。Python 查找属性时先看实例自己的__dict__找不到再去类里找这叫“自底向上查找”。这个机制很容易踩坑。看下面的代码class Counter: count 0 c1 Counter() c2 Counter() c1.count 1 print(c1.count) # 1 print(c2.count) # 0第一次看会以为类属性count是共享计数器c1.count 1应该让所有实例都看到变化吧其实并没有。因为会对c1执行赋值操作这会在c1自己的__dict__里创建一个新的实例属性count覆盖了类属性的查找结果。想在类层面做共享状态应该直接操作type(self).count或者用Counter.count 1。2.4 动手写一个完整的业务类综合上面的知识点我们构建一个更有实际意义的类订单类。它需要校验金额、计算折扣、输出摘要。from datetime import datetime class Order: DISCOUNT_RATES {0: 1.0, 1000: 0.95, 5000: 0.9} def __init__(self, order_id, items, created_atNone): self.order_id order_id self.items items # [{name, price, qty}] self.created_at created_at or datetime.now() self._status pending property def total_amount(self): return sum(item[price] * item[qty] for item in self.items) property def discount_rate(self): rate 1.0 for threshold, discount in sorted(self.DISCOUNT_RATES.items()): if self.total_amount threshold: rate discount return rate property def final_amount(self): return self.total_amount * self.discount_rate def confirm(self): if self._status ! pending: raise ValueError(只有待支付订单可以确认) self._status confirmed def __repr__(self): return fOrder {self.order_id} total{self.final_amount:.2f}这个类用了property把金额计算暴露成属性调用方写order.final_amount而不是order.final_amount()语义上更自然。同时_status被保护起来只能通过confirm()修改这就是封装的价值。3. 方法的三重身份——实例方法、类方法、静态方法3.1 三种方法三种使用场景普通实例方法的第一个参数是self我们对它很熟了。类方法用classmethod装饰第一个参数是cls它操作的是类本身而不是实例。静态方法用staticmethod装饰它与类和实例都没有绑定关系本质上是一个放在类命名空间里的普通函数。选型逻辑可以这样记忆需要访问实例数据或修改实例状态用实例方法。需要获取类属性或创建实例比如做替代构造器用类方法。方法跟当前类有关系但不需要类和实例的数据用静态方法。举一个经典场景从字典创建用户、从文件列表批量创建用户。用类方法作为“工厂”非常顺手。class User: def __init__(self, name, age): self.name name self.age age classmethod def from_dict(cls, data): return cls(namedata[name], agedata[age]) classmethod def from_line(cls, line): name, age line.strip().split(,) return cls(namename, ageint(age)) staticmethod def is_adult(age): return age 18from_dict和from_line就是所谓的“替代构造器”。如果将来User被继承成VipUsercls会自动绑定到VipUser不会出现使用User(...)硬编码导致的子类返回父类问题。3.2 property 装饰器从“方法调用”到“属性访问”property的价值在于最初我们写了一个普通属性self._status后来需求变成“读取状态时要做权限校验”如果外部全都是order._status这样的访问改动就灾难了。但如果一开始就提供property方法外部访问形式是order.status内部逻辑想怎么改都行。property还可以配合 setter 做赋值校验class Product: def __init__(self, name, price): self.name name self._price price property def price(self): return self._price price.setter def price(self, value): if value 0: raise ValueError(价格不能为负数) self._price value这样product.price -100就会直接报错。比在业务逻辑里到处写 if 判断要舒服得多。3.3 选错方法类型的真实教训我见过一个同事把应该定义成staticmethod的一个纯计算逻辑写成了实例方法导致测试时需要实例化整个依赖一堆外部服务的对象才能测。这不仅仅是风格问题更是测试性与模块耦合度的代价。宁可让静态方法显得“不太面向对象”也不要为了形式牺牲可用性。4. 继承、多态与鸭子类型——比想象中更需要克制4.1 该用继承时用继承该用组合时用组合“优先使用组合而非继承”这句话是《设计模式》里的名言但很多人理解偏了。不是说继承不能用而是继承建立的是“is-a”关系组合建立的是“has-a”关系。大部分业务模型里的关系其实都是“has-a”。举个例子一个Car类不能继承自Engine汽车不是一个发动机应该持有self.engine Engine()。反过来SportsCar继承自Car是合理的因为跑车确实是一种汽车。继承最大的问题是层级太深之后父类的任何改动都可能辐射整个体系。我建议继承深度控制在两层到三层超过三层的继承链请停下重构。4.2 super() 到底在做什么super()并不是“调用父类”这么简单它按 MRO方法解析顺序返回下一个类。在单继承里它就是父类在多重继承里它决定顺序的核心。来看一个正确写法class A: def __init__(self, *args, **kwargs): print(A init) super().__init__(*args, **kwargs) class B(A): def __init__(self, *args, **kwargs): print(B init) super().__init__(*args, **kwargs) class C(A): def __init__(self, *args, **kwargs): print(C init) super().__init__(*args, **kwargs) class D(B, C): def __init__(self, *args, **kwargs): print(D init) super().__init__(*args, **kwargs)这个菱形继承的 MRO 是D - B - C - A - object。如果 B 和 C 都调用super().__init__()A 的__init__只会执行一次。这是 Python 新式类协作式多重继承的经典表现。但如果中间有人漏了super()协作链就会断裂A 可能不会初始化。4.3 多态不需要接口也能接口Python 的多态体现在鸭子类型只要对象实现了所需的方法就能传入相应位置。比如定义一组支付处理器class Alipay: def pay(self, amount): print(f支付宝支付 {amount} 元) class WechatPay: def pay(self, amount): print(f微信支付 {amount} 元) class BankCard: def pay(self, amount): print(f银行卡支付 {amount} 元) def checkout(pay_method, amount): pay_method.pay(amount)checkout函数不关心你是什么类只要求你有一个pay方法。这就是面向接口编程而且接口是隐式的。当然隐式接口也有缺点如果传进来的对象没有pay方法运行时才报AttributeError。要提前约束可以用abc模块定义抽象基类或者用typing.Protocol这个在后续部分展开。4.4 多重继承与 MRO 的实操判断多重继承在 Python 中的确存在且合法。但请记住一句话多重继承最容易制造认知负担。一段代码里出现三四个父类时读代码的人很难快速搞明白某个方法的真正来源。如果你真的需要多重继承请分配好每个父类的职责比如一个 mixin 只负责一个横切关注点class LogMixin: def log(self, msg): print(f[LOG] {msg}) class TimeMixin: property def created_at(self): return self._created_at class Task(LogMixin, TimeMixin): pass这里的LogMixin和TimeMixin都是轻量 mixin不参与核心业务逻辑的初始化因此不易冲突。如果两个 mixin 里有同名方法MRO 靠前优先也就是写在继承列表前面的类生效。4.5 鸭子类型与 Protocol动态与静态的平衡点从 Python 3.8 开始typing.Protocol可以描述结构子类型。你不需要强制性继承某个类只要对象拥有协议里声明的方法和属性静态类型检查工具就会认为它符合要求。from typing import Protocol class Payable(Protocol): def pay(self, amount: float) - None: ... class Alipay: def pay(self, amount: float) - None: print(f支付宝支付 {amount} 元) def checkout(pay_method: Payable, amount: float) - None: pay_method.pay(amount)运行时checkout还是完全鸭子类型的。但在编辑器和 mypy 里你能提前发现“这个对象没有 pay 方法”的问题。Protocol 是 Python OOP 生态里被低估的好东西强烈建议在代码量大的项目里推广。5. 魔术方法让你的类融入语言原生世界5.1__repr__与__str__调试体验的分水岭一个没实现__repr__的类在日志里打出来是__main__.Order object at 0x7f...看半天也看不出内容。而实现好的__repr__能让对象在控制台、日志、调试器里直接“说人话”。class User: def __init__(self, name, age): self.name name self.age age def __repr__(self): return fUser(name{self.name!r}, age{self.age!r})!r表示用repr()转义字符串打出来会带引号排版更标准。__str__是给人看的__repr__是给开发者看的。如果只实现一个建议实现__repr__因为 fallback 时__str__会调用__repr__。5.2__eq__与__hash__改了相等就要改哈希两个自定义对象什么时候算相等默认情况下Python 按内存地址比较。如果你想按业务主键例如 user_id比较就需要重写__eq__。但注意重写__eq__后__hash__会被自动设为None这个对象就不可哈希了不能再放进 set 或作为 dict 的 key。解决办法是同时重写class User: def __init__(self, user_id, name): self.user_id user_id self.name name def __eq__(self, other): if not isinstance(other, User): return NotImplemented return self.user_id other.user_id def __hash__(self): return hash(self.user_id) def __repr__(self): return fUser(user_id{self.user_id!r}, name{self.name!r})isinstance(other, User)判断避免跟其他类型比较时报错。NotImplemented是让 Python 尝试反向操作的机制比直接return False更合规。这里有一条原则对象相等性定义在哪套字段上哈希也必须基于同一套字段否则 set 去重会出现“看起来相等但两个都保留”的诡异行为。5.3__enter__与__exit__自定义上下文管理器Python 的with语句依赖__enter__和__exit__。你可以让任何类支持with从而把资源的获取和释放收拢在同一个块里。class ManagedFile: def __init__(self, path, moder): self.path path self.mode mode def __enter__(self): self.file open(self.path, self.mode) return self.file def __exit__(self, exc_type, exc_val, exc_tb): self.file.close()这个比 try/finally 干净得多。注意__exit__的返回值返回True代表异常已被吞掉不建议这么用返回False或None代表异常继续向上抛。5.4 用 dataclass 把样板代码减半从 Python 3.7 开始dataclass装饰器可以根据字段自动生成__init__、__repr__、__eq__等方法。这不是黑魔法只是在类定义阶段帮你写代码。from dataclasses import dataclass dataclass class Product: sku: str name: str price: float 0.0 stock: int 0Product(skuA1, name苹果, price5.5)就能直接用了。字段默认值、排序、冻结模式都有参数可配。frozenTrue可以让实例像元组一样不可变对并发和数据安全更友好。dataclass 非常适合项目里的纯数据模型比手写十几个方法优雅太多。5.5__slots__省内存还是自找麻烦__slots__可以限制实例能绑定的属性名内部使用压缩后的描述符数组存储不再用__dict__每个实例能省相当可观的内存。但代价是不能动态添加新属性调试时也少见__dict__信息。我一般只有在创建几百万个对象、明确内存吃紧时才用它普通业务项目没必要。6. 面向对象设计原则与工程落地——不是背概念是为了活下去6.1 SOLID 用大白话怎么讲很多面试会问 SOLID但工程上它不是用来背的而是用来预判麻烦的单一职责原则SRP一个类只应该有一个改变的理由。说白了别让一个类既管数据库读写、又管消息推送、又管报表生成。开闭原则OCP对扩展开放对修改关闭。加新功能时优先写新类而不是去改一堆已经稳定运行的旧代码。里氏替换原则LSP子类要能无痛替换父类。如果你发现子类里要抛“这个功能不支持”异常大概率继承设计错了。接口隔离原则ISP不要强迫调用方依赖它不需要的方法。依赖倒置原则DIP依赖抽象不要依赖具体实现。工程上最直接的一条建议当你想复制一段旧方法再改两行的时候想想这是不是 OCP 被打破的信号。新增行为应该通过新类或新参数来完成而不是把旧函数越改越长。6.2 工厂模式把创建对象的逻辑收口当创建对象需要根据条件做差异初始化时简单工厂非常靠谱。比如根据支付方式创建不同的支付处理器class PaymentFactory: _payments {} classmethod def register(cls, key): def decorator(payment_cls): cls._payments[key] payment_cls return payment_cls return decorator classmethod def create(cls, key, *args, **kwargs): if key not in cls._payments: raise ValueError(f不支持的支付方式: {key}) return cls._payments[key](*args, **kwargs) PaymentFactory.register(alipay) class Alipay: def pay(self, amount): print(f支付宝支付 {amount} 元) PaymentFactory.register(wechat) class WechatPay: def pay(self, amount): print(f微信支付 {amount} 元)这种注册式工厂的好处是新增支付方式只需要新增一个被装饰的类不需要改工厂内部逻辑符合开闭原则。业务代码里PaymentFactory.create(alipay).pay(100)完全不需要关心类名是什么。6.3 策略模式在多个算法之间动态切换策略模式解决的问题是“同一操作多种实现运行时切换”。Python 里组件少甚至可以用字典直接实现但为了保持组织清晰我还是推荐定义一个策略基类或用 Protocol。class DiscountStrategy: def apply(self, total: float) - float: raise NotImplementedError class NoDiscount(DiscountStrategy): def apply(self, total: float) - float: return total class FullReduction(DiscountStrategy): def __init__(self, threshold: float, reduction: float): self.threshold threshold self.reduction reduction def apply(self, total: float) - float: return total - self.reduction if total self.threshold else total class PercentDiscount(DiscountStrategy): def __init__(self, percent: float): self.percent percent def apply(self, total: float) - float: return total * (1 - self.percent)业务逻辑在use_discount里引用策略抽象类型具体用哪种策略在运行时由外层决定中间零改动这就是策略带来“无侵入扩展”的底气。6.4 依赖注入不再被全局变量绑架依赖注入的核心是“把依赖从内部创建改成外部传入”。原因很简单外部传入的对象可以替换成 mock测试因此不需要起数据库、调外部接口。class ReportService: def __init__(self, db_client, notifier): self.db_client db_client self.notifier notifier def generate_and_notify(self, report_id): data self.db_client.fetch(report_id) self.notifier.send(f报告 {report_id} 已生成共 {len(data)} 条)测试时可以传内存数据库和 fake notifier不必碰真实环境。框架层面推荐dependency-injector但小项目手动在入口组装依赖更轻。关键是不要让业务类内部出现import xx_client; self.client xx_client()这种硬编码。7. OOP 中最容易踩的九个坑——含调试思路7.1 可变默认参数一个老掉牙但还在伤人的问题class ShoppingCart: def __init__(self, items[]): self.items items这个类有两个问题一是默认列表只创建一次所有没传 items 的实例共享同一个列表二是即使每次实例化时传新列表如果不拷一份外部的修改也会影响对象内部。正确做法是class ShoppingCart: def __init__(self, itemsNone): self.items list(items or [])记住一个原则默认参数只用不可变对象涉及容器就复制一遍。7.2 名称改写并不是私有__name双下划线开头会触发名称改写在类内部自动变成_ClassName__name。它是为了防止子类不小心覆盖父类的属性而不是真正的权限控制。外部依然可以通过obj._ClassName__name访问。单下划线才是我们约定俗成的“私有”标记它不会做任何改写。设计 API 的时候想让外部禁止访问就设计成不对外暴露入口而不是靠双下划线防君子不防小人。7.3 继承层级过深导致的行为灾难一个类继承链上有七层父类每个父类里又有一堆 mixin最后这个对象的dir()打出来几百个方法没人能确定某个方法到底在哪一层被谁覆盖了。此时最简单的重构方案是找出代码里真正复用稳定的部分用组合代替深继承让对象持有策略/服务对象而不是反复继承。7.4 循环导入类之间的“先有鸡还是先有蛋”两个模块互相 import 对方时会触发循环导入。解决方案有几个把公共的基础类型抽到第三个模块。在函数内部延迟 import。把 import 放在模块底部临时救火不推荐长期使用。设计阶段如果能意识到类之间双向依赖往往说明职责分配有误优先从设计上消除双向引用。7.5 调试实战使用 vars、dir 和 inspect调试实例时最实用的三个内置工具vars(obj) # 返回实例的 __dict__只看实例属性 dir(obj) # 列出该对象能访问的所有属性含继承来的 inspect.getmro(cls) # 查看类的 MRO理清继承链遇到“这个属性为什么不是我想象的值”时先打印vars(obj)确认到底是实例属性覆盖了类属性还是子类覆盖了父类方法。很多诡异的 bug 在看清这两层后就已经找到答案了。7.6 拷贝陷阱浅拷贝和深拷贝自定义类里如果包含可变容器copy.copy是浅拷贝内外层对象仍然共享内部列表引用。修改副本的列表数据会影响到原对象。用copy.deepcopy可以解决但要求对象内部所有属性都是可深拷贝的。更稳妥的方案是在类的__init__里就对入参容器做复制需求方传什么来都不怕。8. 一个能直接跑的综合案例用户与订单的完整服务代码很多教程拆开讲语法都能看懂一落到综合项目就懵。我把前面所有内容捏合成一个小型案例。from dataclasses import dataclass, field from typing import List, Optional from datetime import datetime from abc import ABC, abstractmethod class DiscountStrategy(ABC): abstractmethod def apply(self, total: float) - float: pass class NoDiscount(DiscountStrategy): def apply(self, total: float) - float: return total class VipDiscount(DiscountStrategy): def __init__(self, percent: float): self.percent percent def apply(self, total: float) - float: return total * (1 - self.percent) dataclass class Product: sku: str name: str price: float dataclass class OrderItem: product: Product qty: int property def subtotal(self) - float: return self.product.price * self.qty dataclass class Order: order_id: str items: List[OrderItem] field(default_factorylist) discount: Optional[DiscountStrategy] None created_at: datetime field(default_factorydatetime.now) _status: str field(defaultpending, initFalse, reprFalse) def add_item(self, product: Product, qty: int) - None: self.items.append(OrderItem(product, qty)) property def total(self) - float: return sum(item.subtotal for item in self.items) property def final_total(self) - float: if self.discount is None: return self.total return self.discount.apply(self.total) def confirm(self) - None: if self._status ! pending: raise RuntimeError(订单只能被确认一次) if self.total 0: raise ValueError(空订单不能确认) self._status confirmed classmethod def from_product_list(cls, order_id: str, product_qty_pairs: List[tuple]) - Order: order cls(order_idorder_id) for product, qty in product_qty_pairs: order.add_item(product, qty) return order def __repr__(self) - str: return fOrder(order_id{self.order_id!r}, total{self.final_total:.2f}, status{self._status})这里面用到了 dataclass、default_factory、property、classmethod、抽象基类、策略模式。你可以在自己的项目里同样组合这些手法不必逐行背下来理解每个零件为什么存在才是关键。Order.from_product_list让我省去在外层写一个OrderFactory的繁琐DiscountStrategy让促销规则可以独立扩展不会污染订单类本身。9. 学 OOP 时最容易被忽略的“心智切换”很多人读了两遍语法书仍然写不出好设计问题出在思维模式没有切换。写脚本时我们思考“先做 A 再调 B 函数判断 C 就输出 D”这是流程思维OOP 要求你思考“谁是对象、对象拥有什么、对象能做什么、谁负责创建谁、谁依赖谁”。我在带人的时候常用一个方法让新人把需求先写成一堆名词再把这些名词标上“是另一个名词吗”还是“拥有另一个名词吗”。前者大概率出继承后者大概率出组合。这个步骤坚持两三个项目后设计能力会有肉眼可见的提升。另一个被忽略的点是OOP 不是以“类多”为荣。过度设计一样是坏味道。如果你的类只有一堆 getter/setter 和一行 pass 的方法那它还没资格成为类可能字典加函数都更直白。好的 OOP 设计应该在“抽象层次”和“实现成本”之间找到平衡点而这个平衡点只能靠持续重构去逼近。10. 我的实操心得与给新手的行动路线我做 Python OOP 重构时一直用一套固定流程先画出模块边界再定义核心类接着用 Protocol 或 ABC 把外部接口钉死最后才写实现。顺序反了后面全是痛苦。如果你正在找工作我建议别只背“什么是多态”这种概念题。亲手写一个“动物叫声”的 demo 没问题但更要关注如何用抽象基类约束子类方法、如何用组合规避菱形继承、如何用工厂把创建逻辑收口、如何用__repr__让日志好排查。这些能把“我会 OOP”从口号变成实打实的能力。对只是想提升编码水平的朋友我的建议很简单找出你手头代码里被大量复制粘贴的函数尝试用策略模式或依赖注入重构一层然后把两个版本的代码量、改动影响面、可测试性对比一下。做过一次你就能真正理解今天这篇指南里所有原则在讲什么。其实 OOP 本身不是目的它只是帮助你管理复杂度的一种思维工具。使用得当代码会像一张清晰的地图使用过度或不当代码会成为一座迷宫。希望你在写下一个 class 的时候先停下来想一想这个类到底为什么存在它需要维护什么状态将来最可能怎样变化。带着这些思考去敲代码你踩过的坑自然会变成经验。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价