【设计模式之美】理解什么面向对象(What)、面向对象的作用(Why)、面向如何实现(How)

从大学毕业开始,已经工作了一年多的时间,在一家公司做了一年JAVA工程师(CRUD工程师),最近学习了极客时间的王争老师的《设计模式之美》,深有体会,特此记录一些自己看完文章的总结笔记,方便日后查阅,这里将会每读完一章节的文章后,对对应的文章梳理总结提炼,加深自己的印象,如有侵权,请联系删除,
这一章节将会理解 “设计原则与思想:面向对象”, 会从 是什么,为什么,怎么做三个角度去剖析面向对象这一概念,文章如有不足,欢迎指出,

理解什么面向对象(What)

我们一直说面向对象面向对象,那这个面向对象到底是啥,是我们相亲对象,还是象棋中的一对象,还是我们处的对象,在我们程序中就是我们new 出来的一个对象。然后我们这个对象定义是由我们程序员自己定义,从简单的整数对象到复杂的飞机都可以被我们程序员看作对象,它不仅仅能表示具体是事物,我们还能表示抽象的规则,计划或时间,具体定义来说,
对象:指的是客体,所谓的客体是指客观存在对象实体和主观抽象的概念
面向对象:就是我们要从对象的角度去思考问题,去思考如何解决需求业务的问题,
面向对象编程:面向对象编程是一种编程范式或编程风格,它以类或对象(对象)作为组织代码(思考)的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石

面向对象的作用(Why)

面向对象是为了解决系统的可维护性、可扩展性,可重用性,我们进一步思考,面向对象为什么能解决系统可维护性,可扩展性,可重用性
面向对象产生的历史原因有下面两点:

1、 计算机是帮助人们解决问题的,然而计算机终究是个机器,他只会按照人所写的代码,一步一步的执行下去,最终得到了结果,因此无论程序多么的复杂,计算机总是能轻松应付,结构化编程,就是按照计算机的思维写出的代码,但是人看到这么复杂的逻辑,就无法维护和扩展了。
2、 结构化设计是以功能为目标来设计构造应用系统,这种做法导致我们设计程序时,不得不将客体所构成的现实世界映射到由功能模块组成的解空间中,这种转换过程,背离了人们观察和解决问题的基本思路。
就像下面图示所示,计算机会按照我们给排定的步骤,一步一步的执行
在这里插入图片描述

可见结构化设计在设计系统的时候,无法解决重用、维护、扩展的问题,而且会导致逻辑过于复杂,代码晦涩难懂。于是人们就想,能不能让计算机直接模拟现实的环境,用人类解决问题的思路,习惯,步骤来设计相应的应用程序,这样的程序,人们在读它的时候,会更容易理解,也不需要再把现实世界和程序世界之间来回做转换。
与此同时,人们发现,在现实世界中存在的客体是问题域中的主角,所谓客体是指客观存在的对象实体和主观抽象的概念,这种客体具有属性和行为,而客体是稳定的,行为不稳定的,同时客体之间具有各种联系,因此面向客体编程,比面向行为编程,系统会更稳定,在面对频繁的需求更改时,改变的往往是行为,而客体一般不需要改变,所以我们就把行为封装起来,这样改变时候只需要改变行为即可,主架构则保持了稳定。
在这里插入图片描述

于是面向对象就产生了。

然而人们追求的系统可维护性,可扩展性,可重用性又是怎么在面向对象中体现出来的呢?

首先看看面向对象的特征:

封装:隐藏信息,保护数据,就像上图所示,我们在面向对象编程中,会将现实世界中存在的客体(人),封装成编程中的一个对象,一些内置的信息数据,比如年龄,性别等数据封装在这个对象中,

抽象:隐藏方法的具体是实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的

继承:子类继承父类,可以继承父类的方法及属性,实现了多态以及代码的重用,因此也解决了系统的重用性和扩展性,但是继承破坏了封装,因为他是对子类开放的,修改父类会导致所有子类的改变,因此继承一定程度上又破坏了系统的可扩展性,所以继承需要慎用,只有明确的IS-A关系才能使用,就比如人类是哺乳动物。

多态:接口的多种不同的实现方式即为多态。接口是对行为的抽象,刚才在封装提到,找到变化部分并封装起来,但是封装起来后,怎么适应接下来的变化?这正是接口的作用,接口的主要目的是为不相关的类提供通用的处理服务,我们可以想象一下。比如鸟会飞,但是超人也会飞,通过飞这个接口,我们可以让鸟和超人,都实现这个接口,这就实现了系统的可维护性,可扩展性。

因此面向对象能实现人们追求的系统可维护性,可扩展性,可重用性。面向对象是一种编程思想,起初,“面向对象”是专指在程序设计中采用封装、继承、多态等设计方法,但面向对象的思想已经涉及到软件开发的各个方面,比如现在细分为了面向对象的分析(OOA),面向对象的设计(OOD),面向对象的编程实现(OOP)

面向如何实现(How)

面向对象编程是一张编程范式或编程风格,它以类或对象最为类组织代码的基本单元,并将类或对象作者代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现基石(以对象为中心,而不是以过程为中心),

上面已经解释这四个特性是什么,接下来将会讲解,这四个特性,在JAVA语言编程中,是如何实现

封装: 暴露有限的接口和属性,通过JAVA语言的权限访问控制的语法(private,protected,public)
抽象:接口,抽象类(interface、abstract)
继承:子类继承父类(extends)
多态:子类可以替换父类

这四个特性的也实现了系统可维护性、可扩展性、可重用性,
其中封装提高代码可维护性,降低接口复杂度,提供类的复用性,
抽象提高代码的可扩展性、维护性,降低复杂度,减少细节负担,
继承提高代码的复用,提高代码的可重用性,
多态提高代码的扩展性,子类可以替换父类

面向对象编程步骤:

  1. 面向对象分析(OOA):分析需求,找出客观主体(类),行为(方法),主体之间的关系
  2. 面向对象设计(OOD):画出UML,类图这些,确认类之间的关系(泛化、实现、组合、依赖)
  3. 面向对象编程(OOP):根据设计,结合特性,进行面向对象编程

转变思路
接到需求,第一部不是考虑如何实现需求,而是进行需求分析,根据需求找到其中的实体,再找到这些实体之间的联系,再通过UML画出来,然后再思考通过这些实体,去考虑如何完成你的需求,

面向对象编程与面向过程编程的区别

面向过程编程:面向过程编程也是一种编程范式或编程风格,它以过程(可以理解为方法,函数,操作)作为组织代码的基本单元,以数据(可以理解成为变量,属性)与方法相分析为主要特性,面向过程风格是一样流程化的编程风格,通过凭借一组顺序执行的方法来操作完成一项功能

以下是《设计模式之美》例子说明摘抄

首先,我们先来看,用面向过程这种编程风格写出来的代码是什么样子的。注意,下面的代码是用 C 语言这种面向过程的编程语言来编写的。


struct User {
  char name[64];
  int age;
  char gender[16];
};

struct User parse_to_user(char* text) {
  // 将text(“小王&28&男”)解析成结构体struct User
}

char* format_to_text(struct User user) {
  // 将结构体struct User格式化成文本("小王\t28\t男")
}

void sort_users_by_age(struct User users[]) {
  // 按照年龄从小到大排序users
}

void format_user_file(char* origin_file_path, char* new_file_path) {
  // open files...
  struct User users[1024]; // 假设最大1024个用户
  int count = 0;
  while(1) { // read until the file is empty
    struct User user = parse_to_user(line);
    users[count++] = user;
  }
  
  sort_users_by_age(users);
  
  for (int i = 0; i < count; ++i) {
    char* formatted_user_text = format_to_text(users[i]);
    // write to new file...
  }
  // close files...
}

int main(char** args, int argv) {
  format_user_file("/home/zheng/user.txt", "/home/zheng/formatted_users.txt");
}

然后,我们再来看,用面向对象这种编程风格写出来的代码是什么样子的。注意,下面的代码是用 Java 这种面向对象的编程语言来编写的。


 public class User {
  private String name;
  private int age;
  private String gender;
  
  public User(String name, int age, String gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
  
  public static User praseFrom(String userInfoText) {
    // 将text(“小王&28&男”)解析成类User
  }
  
  public String formatToText() {
    // 将类User格式化成文本("小王\t28\t男")
  }
}

public class UserFileFormatter {
  public void format(String userFile, String formattedUserFile) {
    // Open files...
    List users = new ArrayList<>();
    while (1) { // read until file is empty 
      // read from file into userText...
      User user = User.parseFrom(userText);
      users.add(user);
    }
    // sort users by age...
    for (int i = 0; i < users.size(); ++i) {
      String formattedUserText = user.formatToText();
      // write to new file...
    }
    // close files...
  }
}

public class MainApplication {
  public static void main(String[] args) {
    UserFileFormatter userFileFormatter = new UserFileFormatter();
    userFileFormatter.format("/home/zheng/users.txt", "/home/zheng/formatted_users.txt");
  }
}

面的代码中,我们可以看出,面向过程和面向对象最基本的区别就是,代码的组织方式不同。面向过程风格的代码被组织成了一组方法集合及其数据结构(struct User),方法和数据结构的定义是分开的。面向对象风格的代码被组织成一组类,方法和数据结构被绑定一起,定义在类中。

贫血模型VS充血模型

贫血模型: 数据与业务逻辑被分割到不同的类中 (典型例子:MVC三层结构)
充血模型: 数据和相应业务逻辑被封装到同一个类中(DDD,领域驱动设计)

基于充血模型的DDD开发模式跟基于贫血模型的传统开发模式相比,主要区别在于Service层,在基于充血模型的开发模型下,我们将原来在Service类中的业务逻辑转移到了一个充血Domain领域模型中,让Service类的实现依赖这个类,
基于充血模型的DDD开发模型下,Service类并不完全移除,而是负责放一些不适合放在Domain的功能,比如负责与Reoisitory层打交道,跨领域模型的业务聚合功能,幂等事务等非功能性的工作,
基于充血模型的DDD开发模型跟基于贫血模型的传统开发模型相比,Controller层和Repository层的代码基本相同,这是因为Repostitory层的Entity的生命周期有限,Controller层的VO只是单纯作为DTO,两部分业务逻辑都不会太复杂,所以类同贫血模型没有问题

设计思想

基于接口而非实现编程(基于抽象而非实现编程)
我们在做软件开发的时候,一定要有抽象意识,封装意思,接口意识,越抽象,越顶层,越脱离具体实现的设计,越是能提供代码的灵活性,扩展性,可维护性

多用组合少用继承
继承主要又三个作用,表示is-a关系,支持多态特性,代码复用,而这三个作用都可以通过组合,接口,委托三个技术手段来达成,除此之外,利用组合还能解决层次过深,过复杂的继承关系,影响代码的可维护性问题

Reference

《设计模式之美》:https://time.geekbang.org/column/intro/100039001?tab=catalog
什么是面向对象?为什么要面向对象?怎么样面向对象?:https://mp.weixin.qq.com/s/thCux5W6uTkSaGTMZesPbA

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值