设计模式之美 / 前Google工程师手把手教你写高质量代码
王争
前Google工程师,《数据结构与算法之美》专栏作者
 
  • 课程目录
  • 课程介绍
  • 开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!

    如果说“数据结构与算法之美”是教你如何写出高效的代码,那这个设计模式专栏就是教你如何写出高质量的代码。

  • 01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?

    设计模式作为一门与编码、开发有着直接关系的基础知识,是你现在就要开始学习的。

  • 02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?

    辨别代码写得“好”还是“烂”,是写出好代码的前提。如果连什么是好代码、什么是烂代码,都分辨不清,又谈何写出好代码呢?

  • 03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?

    今天的内容相当于专栏的一个教学大纲或者学习框架,能让你对整个专栏的知识点有一个全局的了解。

  • 04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?

    专栏正文的第一个模块,我会讲一些设计原则、设计思想,为之后学习设计模式做铺垫。

  • 05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?

    理解面向对象编程及面向对象编程语言的关键就是理解其四大特性:封装、抽象、继承、多态。

  • 06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?

    在过往的工作中,我发现很多人搞不清面向对象和面向过程的区别,总以为使用面向对象编程语言来做开发,就是在进行面向对象编程。

  • 07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?

    面向过程编程真的无用武之地了吗?有必要杜绝在面向对象编程中写面向过程风格的代码吗?

  • 08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?

    你熟悉的编程语言,是否有现成的语法支持接口和抽象类呢?具体是如何定义的呢?

  • 09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?

    我们继续讲“基于接口而非实现编程”。这个原则是一种非常有效的提高代码质量的手段,在平时的开发中特别经常被用到。

  • 10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?

    组合不是完美的,继承也并非一无是处。在实际的项目开发中,我们要根据具体情况,来选择该用继承还是组合。

  • 11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?

    对于业务不复杂的系统开发,基于贫血模型的传统开发模式简单够用;如果是复杂的,那基于充血模型的DDD开发模式更有优势。

  • 12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?

    今天我们看,如何用基于贫血模型的传统开发模式和基于充血模型的DDD开发模式,来设计实现一个钱包系统。

  • 13 | 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?

    需求分析的过程实际上是一个不断迭代优化的过程。不要试图一下就给出一个完美的解决方案。

  • 14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?

    面向对象分析的产出是详细的需求描述。面向对象设计的产出是类。在面向对象设计这一环节,我们将需求描述转化为具体的类的设计。

  • 15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?

    不同的应用场景、不同阶段的需求背景、不同的业务层面,对同一个类的职责是否单一,可能会有不同的判定结果。

  • 16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?

    开闭原则是SOLID中最难理解、最难掌握,同时也是最有用的一条原则。

  • 17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?

    整体上来讲,这个设计原则是比较简单、容易理解和掌握的。今天我主要通过几个反例,带你看看,哪些代码是违反里式替换原则的。

  • 18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?

    接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。

  • 19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?

    控制反转是一个比较笼统的设计思想,并不是一种具体的实现方法,一般用来指导框架层面的设计。

  • 20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?

    代码行数越少并不代表代码越简单,我们还要考虑逻辑复杂度、实现难度、代码的可读性等。

  • 21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?

    我们今天讲三种代码重复的情况:实现逻辑重复、功能语义重复、代码执行重复。

  • 22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?

    “高内聚、松耦合”是一个非常重要的设计思想,能够有效提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。

  • 23 | 实战一(上):针对业务系统的开发,如何做需求分析和设计?

    技术人也要有一些产品思维。对于产品设计、需求分析,我们要学会“借鉴”,一定不要自己闷头想。

  • 24 | 实战一(下):如何实现一个遵从设计原则的积分兑换系统?

    从表面上看,做业务开发可能不是特别有技术挑战。实际上,如果要做到透彻理解,并不是件容易的事情。

  • 25 | 实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?

    今天我们分享几个设计复杂框架小技巧:画产品线框图、聚焦简单应用场景、设计实现最小原型、画系统设计图。

  • 26 | 实战二(下):如何实现一个支持各种统计规则的性能计数器?

    写代码的过程本就是一个修修改改、不停调整的过程,肯定不是一气呵成的。所以,迭代思维很重要,不要刚开始就追求完美。

  • 27 | 理论一:什么情况下要重构?到底重构什么?又该如何重构?

    今天的内容主要是让你建立持续重构意识。很多技术问题不是单纯靠技术来解决的,更重要的是要有意识。

  • 28 | 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?

    你参与的项目有没有写单元测试?单元测试是否足够完备?贯彻执行写单元测试的过程中,遇到过哪些问题?又是如何解决的?

  • 29 | 理论三:什么是代码的可测试性?如何写出可测试性好的代码?

    依赖注入是编写可测试性代码的最有效手段。

  • 30 | 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?

    过于复杂的代码往往在可读性、可维护性上都不友好。解耦保证代码松耦合、高内聚,是控制代码复杂度的有效手段。

  • 31 | 理论五:让你最快速地改善代码质量的20条编程规范(上)

    我根据自己的开发经验,总结罗列了20条我个人觉得最好用的编码规范。掌握这20条编码规范,能你最快速地改善代码质量。

  • 32 | 理论五:让你最快速地改善代码质量的20条编程规范(中)

    我们其实很难说哪种代码风格更好。在团队、项目中保持风格统一,让代码像同一个人写出来的。这才是实际工作中要实现的目标。

  • 33 | 理论五:让你最快速地改善代码质量的20条编程规范(下)

    上两节课,我们讲了命名和注释、代码风格,今天我们来讲一些比较实用的编程技巧,帮你切实地提高代码可读性。

  • 34 | 实战一(上):通过一段ID生成器代码,学习如何发现代码质量问题

    今天我重点讲一个问题,如何发现代码质量问题?给你准备了我整理的发现代码质量问题的两个checklist。

  • 35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”

    我带你将小王写的凑活能用的代码,重构成结构更加清晰、更加易读、更易测试的代码,并且为其补全单元测试。

  • 36 | 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?

    对于函数出错返回数据类型,我总结了4种情况,它们分别是:错误码、NULL值、空对象、异常对象。

  • 37 | 实战二(下):重构ID生成器项目中各函数的异常处理代码

    再简单的代码,看上去再完美的代码,只要我们下功夫去推敲,总有可以优化的空间,就看你愿不愿把事情做到极致。

  • 38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点

    到今天为止,设计原则和思想已经全部讲完了。内容比较多,我来带你复习一下,你可以看看自己的掌握程度。

  • 39 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(上)

    前面我们实现了框架的第一个版本,只包含最基本的一些功能。今天我针对这些不足,继续迭代开发第二个版本。

  • 40 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(下)

    今天我们继续完善框架的功能和非功能需求,让它成为一个能用且好用的框架。

  • 41 | 单例模式(上):为什么说支持懒加载的双重检测不比饿汉式更优?

    如果随便抓一个程序员,让他说一说最熟悉的3种设计模式,那其中肯定会包含今天要讲的单例模式。

  • 42 | 单例模式(中):我为什么不推荐使用单例模式?又有何替代方案?

    单例这种设计模式存在哪些问题?为什么会被称为反模式?如果不用单例,该如何表示全局唯一类?有何替代的解决方案?

  • 43 | 单例模式(下):如何设计实现一个集群环境下的分布式单例模式?

    进程唯一意味着线程内、线程间都唯一,这也是进程唯一和线程唯一的区别之处。集群唯一指的是进程内唯一、进程间也唯一。

  • 44 | 工厂模式(上):我为什么说没事不要随便用工厂模式创建对象?

    今天讲三种工厂模式,其中简单工厂和工厂方法比较常用,抽象工厂的应用场景比较特殊,很少用到。

  • 45 | 工厂模式(下):如何设计实现一个Dependency Injection框架?

    一个简单的DI容器的实现原理,其核心逻辑主要包括:配置文件解析、根据配置文件通过“反射”语法来创建对象。

  • 46 | 建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式

    只有了解了这些最本质的东西,我们才能不生搬硬套,才能灵活应用,甚至可以混用各种模式创造出新的模式,来解决特定场景的问题。

  • 47 | 原型模式:如何最快速地clone一个HashMap散列表?

    前面我们已经讲了单例模式、工厂模式、建造者模式,今天我们来讲最后一个创建型模式:原型模式。

  • 48 | 代理模式:代理在RPC、缓存、监控等场景中的应用

    从今天起,我们开始学习另外一种类型的设计模式:结构型模式。今天讲第一个,代理模式。

  • 49 | 桥接模式:如何实现支持不同类型和渠道的消息推送系统?

    桥接模式的原理比较难理解,但代码实现相对简单。

  • 50 | 装饰器模式:通过剖析Java IO类库源码学习装饰器模式

    装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。

  • 51 | 适配器模式:代理、适配器、桥接、装饰,这四个模式有何区别?

    适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”。

  • 52 | 门面模式:如何设计合理的接口粒度以兼顾接口的易用性和通用性?

    完成接口设计,就相当于完成了一半的开发任务。只要接口设计得好,那代码就差不到哪里去。

  • 53 | 组合模式:如何设计实现支持递归遍历的文件系统目录树结构?

    组合模式,将一组对象组织成树形结构,将单个对象和组合对象都看做树中的节点,并利用树形结构的特点,简化代码实现。

  • 54 | 享元模式(上):如何利用享元模式优化文本编辑器的内存占用?

    享元模式的意图是复用对象,节省内存,前提是享元对象是不可变对象。

  • 55 | 享元模式(下):剖析享元模式在Java Integer、String中的应用

    除非经过线上验证,利用享元模式真的可以大大节省内存,否则就不要过度使用这个模式。

  • 56 | 观察者模式(上):详解各种应用场景下观察者模式的不同实现方式

    设计模式要干的事情就是解耦,创建型模式是将创建和使用代码解耦,结构型模式是将不同功能代码解耦,那观察者模式呢?

  • 57 | 观察者模式(下):如何实现一个异步非阻塞的EventBus框架?

    在平时的业务开发中,我们要善于抽象非业务的、可复用的功能,并积极地把它们实现成通用的框架。

  • 58 | 模板模式(上):剖析模板模式在JDK、Servlet、JUnit等中的应用

    模板模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。

  • 59 | 模板模式(下):模板模式与Callback回调函数有何区别和联系?

    回调可以细分为同步回调和异步回调。从应用场景上来看,同步回调看起来更像模板模式,异步回调看起来更像观察者模式。

  • 60 | 策略模式(上):如何避免冗长的if-else/switch分支判断代码?

    策略模式用来解耦策略的定义、创建、使用。实际上,一个完整的策略模式就是由这三个部分组成的。

  • 61 | 策略模式(下):如何实现一个支持给不同大小文件排序的小程序?

    设计原则和思想其实比设计模式更加普适和重要,掌握了代码的设计原则和思想,我们甚至可以自己创造出来新的设计模式。

  • 62 | 职责链模式(上):如何实现可灵活扩展算法的敏感信息过滤框架?

    职责链模式有两种常用的实现。一种是使用链表来存储处理器,另一种是使用数组来存储处理器,后面一种实现方式更加简单。

  • 63 | 职责链模式(下):框架中常用的过滤器、拦截器是如何实现的?

    职责链模式常用在框架开发中实现框架的过滤器、拦截器功能,让框架的使用者在不需要修改框架源码的情况下添加新的过滤拦截功能。

  • 64 | 状态模式:游戏、工作流引擎中常用的状态机是如何实现的?

    虽然网上有各种状态模式的定义,但是你只要记住状态模式是状态机的一种实现方式即可。

  • 65 | 迭代器模式(上):相比直接遍历集合数据,使用迭代器有哪些优势?

    迭代器模式,也叫游标模式。它用来遍历集合对象。一个完整的迭代器模式,一般会涉及容器和容器迭代器两部分内容。

  • 66 | 迭代器模式(中):遍历集合的同时,为什么不能增删集合元素?

    用迭代器遍历集合元素的同时增删集合中的元素,可能会导致某个元素被重复遍历或遍历不到。不过,并不是所有情况下都会遍历出错。

  • 67 | 迭代器模式(下):如何设计实现一个支持“快照”功能的iterator?

    这一节的问题算是对上一节课内容的延伸思考,为的是帮你加深对迭代器模式的理解,也是对你分析、解决问题的一种锻炼。

  • 68 | 访问者模式(上):手把手带你还原访问者模式诞生的思维过程

    访问者模式的设计意图是解耦操作和对象本身,保持类职责单一、满足开闭原则以及应对代码的复杂性。

  • 69 | 访问者模式(下):为什么支持双分派的语言不需要访问者模式?

    访问者模式难以理解,应用场景有限,不是特别必需,我不建议在项目中使用它。

  • 70 | 备忘录模式:对于大对象的备份和恢复,如何优化内存和时间的消耗?

    备忘录模式的应用场景也比较明确和有限,主要是用来防丢失、撤销、恢复等。它跟平时我们常说的“备份”很相似。

  • 71 | 命令模式:如何利用命令模式实现一个手游后端架构?

    命令模式的主要作用是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等。

  • 72 | 解释器模式:如何设计实现一个自定义接口告警规则功能?

    解释器模式代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。

  • 73 | 中介模式:什么时候用中介模式?什么时候用观察者模式?

    如果你对中介模式有所了解,你可能会知道,它跟观察者模式有点相似,所以,今天我们会详细讲一下这两种模式的区别。

  • 74 | 总结回顾23种经典设计模式的原理、背后的思想、应用场景等

    到今天为止,23种经典的设计模式已经全部讲完了。今天,我逐一带你回顾一下每一种设计模式的原理、实现、设计意图和应用场景。

  • 75 | 在实际的项目开发中,如何避免过度设计?又如何避免设计不足?

    今天我想和你聊一下,在实际的项目开发中,如何避免过度设计,以及如何避免设计不足。

  • 76 | 开源实战一(上):通过剖析Java JDK源码学习灵活应用设计模式

    今天我重点讲了四种设计模式在Java JDK中的应用,主要是给你展示真实项目中是如何灵活应用设计模式的。

  • 77 | 开源实战一(下):通过剖析Java JDK源码学习灵活应用设计模式

    今天我们继续延续上一节的话题,再重点讲一下模板模式、观察者模式这两个模式在JDK中的应用。

  • 78 | 开源实战二(上):从Unix开源开发学习应对大型复杂项目开发

    如何应对复杂软件开发?Unix开源项目就是一个值得学习的例子。

  • 79 | 开源实战二(中):从Unix开源开发学习应对大型复杂项目开发

    找到对的人、用对好的人,打造优秀的技术文化,才是一直保持卓越的根本。

  • 80 | 开源实战二(下):从Unix开源开发学习应对大型复杂项目开发

    为什么要进行Code Review?Code Review的价值在哪里?

  • 81 | 开源实战三(上):借Google Guava学习发现和开发通用功能模块

    我们对Google Guava做一个简单介绍,并借此讲一下如何开发一个通用的功能模块。

  • 82 | 开源实战三(中):剖析Google Guava中用到的几种设计模式

    今天我们来学习下Google Guava中的设计模式:Builder模式、Wrapper模式和Immutable模式。

  • 83 | 开源实战三(下):借Google Guava学习三大编程范式中的函数式编程

    越来越多的编程语言开始支持函数式编程,但我觉得它只能用在一些特殊的领域,没法完全替代面向对象、面向过程编程范式。

  • 84 | 开源实战四(上):剖析Spring框架中蕴含的经典设计思想或原则

    Spring框架中蕴含的一些设计思想或原则都很通用,掌握之后我们可以借鉴用到其他框架的开发中。

  • 85 | 开源实战四(中):剖析Spring框架中用来支持扩展的两种设计模式

    观察者模式在Java、Google Guava、Spring中都提供了实现代码。基于代码,我们可以实现观察者模式。

  • 86 | 开源实战四(下):总结Spring框架用到的11种设计模式

    Spring框架中用到的设计模式非常多,不下十几种。我们今天就总结一下。

  • 87 | 开源实战五(上):MyBatis如何权衡易用性、性能和灵活性?

    除了对MyBatis做背景介绍之外,我还会和你说一说代码的易用性、性能、灵活性之间的关系。

  • 88 | 开源实战五(中):如何利用职责链与代理模式实现MyBatis Plugin?

    至此我们已经学习了三种职责链常用的应用场景:过滤器、拦截器、插件。

  • 89 | 开源实战五(下):总结MyBatis框架中用到的10种设计模式

    MyBatis对很多设计模式的实现都并非标准的代码实现,这就是所谓的灵活应用,只借鉴不照搬,根据具体问题针对性地去解决。

  • 90 | 项目实战一:设计实现一个支持各种算法的限流框架(分析)

    今天我们主要对限流框架做个大的背景介绍,明确一下要做什么,为下两节课的设计和实现做准备。

  • 91 | 项目实战一:设计实现一个支持各种算法的限流框架(设计)

    我们将这个限流框架划分为限流规则、限流算法、限流模式、集成使用者这四个模块来分析讲解。

  • 92 | 项目实战一:设计实现一个支持各种算法的限流框架(实现)

    优秀的代码是重构出来的,复杂的代码是慢慢堆砌出来的。

  • 93 | 项目实战二:设计实现一个通用的接口幂等框架(分析)

    大部分情况下,如果接口只包含查询、删除、更新这些操作,那接口天然是幂等的。

  • 94 | 项目实战二:设计实现一个通用的接口幂等框架(设计)

    在正常情况下,幂等框架的处理流程是比较简单的,调用方生成幂等号,传递给实现方,实现方记录幂等号或者用幂等号判重。

  • 加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法

    考虑到有些同学并不熟悉Java语言,我今天用一篇文章介绍一下专栏中用到的Java语法。

  • 加餐二 | 设计模式、重构、编程规范等相关书籍推荐

    如果你想要学好设计模式,肯定是要看书的。我结合自己的经验,为你打造了一份读书单,配合专栏一块儿学,效果会更好!

  • 春节特别加餐 | 王争:如何学习《设计模式之美》专栏?

    你好,我是王争。祝你春节快乐,身体健康!

【仅限 100 个名额 | 立省 ¥80】

拼团+口令「maimaimai」到手仅 ¥119立省 ¥80,仅限 100 个名额

你将获得

  • 23 种设计模式与范式实战精讲;
  • 200+ 真实案例分析设计与实现;
  • 顶尖互联网公司的编程经验分享;
  • 应对设计模式面试的思路与技巧。

讲师介绍

王争,《数据结构与算法之美》作者,前Google工程师,从事Google翻译、知识图谱等相关系统的开发。曾任某金融公司核心系统资深系统架构师,负责公司核心业务的架构设计和开发工作。工作十多年,干过架构、做过产品、带过团队、创过业,最后发现还是最喜欢写代码,始终没有脱离编码第一线。

课程介绍

设计模式对你来说,应该不陌生。在面试中,经常会被问到;在工作中,有时候也会用到。一些设计模式书籍,比如大名鼎鼎的GoF的《设计模式》、通俗易懂的《Head First设计模式》,估计你也都研读过。那你是否觉得自己已经掌握了设计模式呢?是否思考过怎么才算真正掌握了设计模式呢?是熟练掌握每种设计模式的原理和代码实现吗?

搞懂23种经典的设计模式,并不是件难事。你随便找本书看看就差不多了。难的是如何不生搬硬套、恰到好处地将其应用到实际的项目中。即便如此,这也并不是我们的最终目标。毕竟设计模式只是解决问题的一个方法,我们最终的目标还是要写出高质量的代码。

单纯学习设计模式,并不能让你写出更好的代码。这就像单纯地了解编程语言的语法,也不能算是会写代码一样。单纯看书,对于设计模式的掌握、代码能力的锻炼,你只能达到10%,剩下的90%还是要靠在实战中刻意练习。

而大部分工程师可能都是偏业务开发,在平时做的项目中,很少有高密度地使用各种设计模式的机会,所以这方面的锻炼肯定不多。

因此,王争结合自己过去十多年工作中积累的项目经验,为每节课、每个知识点都设计了真实的代码实例。希望用8个月的时间,通过整个专栏200多个实战案例,手把手带你高强度、刻意地练习设计模式,潜移默化地提高你的设计编码能力,教会你如何编写高质量代码,帮你跨过知识到应用的鸿沟。

一、专栏会讲哪些知识?

整个专栏以23种设计模式为核心,从面向对象、设计原则、编程规范、代码重构铺开,带你追本溯源,一次性全面掌握编写高质量代码的所有知识。下面是专栏的知识概览图。

二、专栏模块是怎么设置的?

专栏共100期正文和10期不定期加餐,分为5个模块。

学习导读部分,首先帮你明确设计模式知识的实际用途,帮你梳理最重要、最常用的7大代码评判标准,带你认识整个课程的知识框架,明确学习的任务,为后面的具体学习做好准备。

设计原则与思想部分,将为你详细讲解面向对象、设计原则、编码规范、重构技巧等基础编码知识。每一个知识点分别通过“理论篇”来精讲,通过“实战篇”带你应用,通过“总结篇”带你复习巩固。

设计模式与范式部分,将精讲23种经典设计模式,帮你搞懂每一种设计模式的原理、核心思想和应用场景,并告诉你如何避免过度设计和设计不足,一次性彻底掌握设计模式相关的所有知识。

开源与项目实战部分,将带你剖析6个常用开源框架用到的设计模式、原则和思想,并通过完整的分析、设计和实现过程,手把手带你完成4个实战项目,将学过的理论知识应用到实战中。

加餐部分,将随专栏进度不定期进行更新。这一部分将和你分享作者十多年工作中总结出的学习方法、工作心得和人生经验。

课程目录

适合人群

1.专栏中的代码是用 Java 语言实现的,但是专栏内容的讲解并不与具体的编程语言挂钩。只要你熟悉一门编程语言即可。

2.专栏重点面向偏后端的程序员,有一定项目经验会更好,没有也完全没有关系。

特别放送

订阅须知