设计原则与思想:面向对象

一. 面向对象的四大特性

封装(Encapsulation)

  • 定义:封装就是隐藏属性,通过暴露有限的接口供外部访问;
  • 语法机制:访问权限控制(private、protected、public);
  • 意义:
    1. 如果不对属性访问修改做权限控制,增加了灵活性但也意味着不可控。但也意味着属性可以被各种方式修改、而且修改逻辑可能散落在代码中的各个角落,势必影响代码的可读性、可维护性。
    2. 有限的方法暴露提供类的易用性。暴露的接口越少,使用者需要理解、操作的东西也就越少,不需要对业务细节有足够的了解。

抽象(Abstraction)

  • 定义:隐藏方法的具体实现、让调用者只需要关心方法提供了哪些功能;
  • 语法机制:可以通过接口类或者抽象类来实现,但也并不需要特殊的语法机制来支持,函数可看做是实现抽象的语法机制;
  • 意义:
    1. 抽象作为一种只关注功能点不关注实现的设计思想,可以帮我们大脑过滤掉许多非必要的信息;
    2. 抽象作为一个非常宽泛的设计思想,在代码设计中,起到非常重要的指导作用。很多设计原则都体现了抽象这种设计思想,比如基于接口而非实现编程、开闭原则(对扩展开放、对修改关闭)、代码解耦(降低代码的耦合性)等。

继承 (Inheritance)

  • 定义:继承是用来表示类之间的 is-a 关系,比如猫是一种哺乳动物;
  • 语法机制:extends;
  • 意义:代码复用、符合人类认知,有结构美感;

多态(Polymorphism)

  • 定义:子类可以替换父类;
  • 语法机制:继承 + 方法重写
  • 意义:
    1. 提高代码的可扩展性和复用性;
    2. 多态也是很多设计模式、设计原则、编程技巧的代码实现基础,比如策略模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等。

二. 违反面向对象的典型代码设计

  1. 滥用 getter、setter 方法
    在设计实现类的时候,除非真的需要,否则尽量不要给属性定义 setter 方法。除此之外,尽管 getter 方法相对 setter 方法要安全些,但是如果返回的是集合容器,那也要防范集合内部数据被修改的风险。
  2. Constants 类、Utils 类的设计问题
    对于这两种类的设计,我们尽量能做到职责单一,定义一些细化的小类,比如 RedisConstants、FileUtils,而不是定义一个大而全的 Constants 类、Utils 类。除此之外,如果能将这些类中的属性和方法,划分归并到其他业务类中,那是最好不过的了,能极大地提高类的内聚性和代码的可复用性。
  3. 基于贫血模型的开发模式
    MVC设计模式,WEB项目常用的开发模式。

三.抽象类和接口

  1. 定义
    抽象类:不允许实例化。只能被继承。可以包含成员属性和方法。方法既可以包含代码实现也可以不包含代码实现。子类继承抽象类,必须实现抽象类中的所有抽象方法。
    接口:不允许实例化,只能被实现。不能包含成员属性(只能包含类属性 statci final),只能声明方法。方法不能包含代码实现。类实现接口的时候,必须实现接口中声明的所有方法。
  2. 意义:
    抽象类:解决复用问题,适用于is-a的关系。
    接口:适用于behaves-like的关系,表示具有一组行为特征,是为了解决解耦问题,隔离接口和具体的实现,提高代码的扩展性。
    3.应用场景
    根据存在的意义来。

四. 基于接口而非实现编程

  1. “基于接口而非实现编程”,这条原则的另一个表述方式,是“基于抽象而非实现编程”。后者的表述方式其实更能体现这条原则的设计初衷。我们在做软件开发的时候,一定要有抽象意识、封装意识、接口意识。越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性、扩展性、可维护性。2. 我们在定义接口的时候,一方面,命名要足够通用,不能包含跟具体实现相关的字眼;另一方面,与特定实现有关的方法不要定义在接口中。3.“基于接口而非实现编程”这条原则,不仅仅可以指导非常细节的编程开发,还能指导更加上层的架构设计、系统设计等。比如,服务端与客户端之间的“接口”设计、类库的“接口”设计。

  2. 我们在定义接口的时候,一方面,命名要足够通用,不能包含跟具体实现相关的字眼;另一方面,与特定实现有关的方法不要定义在接口中。

  3. “基于接口而非实现编程”这条原则,不仅仅可以指导非常细节的编程开发,还能指导更加上层的架构设计、系统设计等。比如,服务端与客户端之间的“接口”设计、类库的“接口”设计。

五. 多用组合少用继承

  1. 继承为解决代码复用问题。若继承层次过深、过复杂,会影响代码可维护性。这种情况下应该少用继承;
  2. 继承的特性,我们完全可以通过组合、接口、委托三个技术手段来达成。

六. 贫血模型和基于充血模型的DDD

  1. 基于充血模型的 DDD 开发模式跟基于贫血模型的传统开发模式相比,主要区别在 Service 层。在基于充血模型的开发模式下,我们将部分原来在 Service 类中的业务逻辑移动到了一个充血的 Domain 领域模型中,让 Service 类的实现依赖这个 Domain 类。
  2. 在基于充血模型的 DDD 开发模式下,Service 类并不会完全移除,而是负责一些不适合放在 Domain 类中的功能。比如,负责与 Repository 层打交道、跨领域模型的业务聚合功能、幂等事务等非功能性的工作。

七. 面向对象设计

1. 划分职责进而识别出有哪些类
根据需求描述,我们把其中涉及的功能点,一个一个罗列出来,然后再去看哪些功能点职责相近,操作同样的属性,可否归为同一个类。
2. 定义类及其属性和方法
我们识别出需求描述中的动词,作为候选的方法,再进一步过滤筛选出真正的方法,把功能点中涉及的名词,作为候选属性,然后同样再进行过滤筛选。
3. 定义类与类之间的交互关系
UML 统一建模语言中定义了六种类之间的关系。它们分别是:泛化、实现、关联、聚合、组合、依赖。我们从更加贴近编程的角度,对类与类之间的关系做了调整,保留四个关系:泛化、实现、组合、依赖。
4. 将类组装起来并提供执行入口
我们要将所有的类组装在一起,提供一个执行入口。这个入口可能是一个 main() 函数,也可能是一组给外部用的 API 接口。通过这个入口,我们能触发整个代码跑起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值