龙空技术网

设计模式之"你低估了的面向对象"编程

小龙飞飞飞 65

前言:

现在各位老铁们对“php面向对象程序设计的优缺点”可能比较关注,各位老铁们都需要学习一些“php面向对象程序设计的优缺点”的相关内容。那么小编也在网上汇集了一些对于“php面向对象程序设计的优缺点””的相关内容,希望兄弟们能喜欢,我们一起来了解一下吧!

目录:

三大编程范式函数式编程面向过程编程面向对象编程面向对象编程的优点适合大规模复杂项目模块化组织代码封装、抽象、继承、多态面向对象语言丰富指导落地案例丰富封装、继承、多态、抽象封装抽象继承多态写这个的原因三大编程范式函数式编程

编程范式

这张图我总结了面向对象编程、面向过程编程、以及函数式编程最核心的内容点,看完差不多就能明白他们三者之间的核心内容了。

鉴于上面的概念性总结还是有点抽象,这里我想给出例子,最直接的感受下面向对象编程风格和面向对象编程风格的不同:

举个栗子: 假设我们有一个记录了用户信息的文本文件 users.txt,每行文本的格式是 name&age&gender(比如,小曾&24&女)。我们希望写一个程序,从 users.txt 文件中逐行读取用户信息,然后格式化成 "name"\t"age"\t"gender"(其中,\t 是分隔符)这种文本格式,并且按照 age 从小到大排序之后,重新写入到另一个文本文件 formatted_users.txt 中。

面向过程编程

「C语言实现的面向过程编程风格」

struct User {  char name[64];  int age;  char gender[16];};struct User parse_to_user(char* text) {  // 将text(“小曾&24&女”)解析成结构体struct User}char* format_to_text(struct User user) {  // 将结构体struct User格式化成文本("小曾\t24\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/zeng/user.txt", "/home/zeng/formatted_users.txt");}

面向过程风格的代码被组织成了一组方法集合及其数据结构(struct User)

面向对象编程

「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(“小曾&24&女”)解析成类User  }    public String formatToText() {    // 将类User格式化成文本("小曾\t24\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/zeng/users.txt", "/home/zeng/formatted_users.txt");  }}

需要三个类,一个是实体类,一个用户文件解析类,一个主类。

面向对象风格的代码被组织成一组类,方法和数据结构被绑定一起,定义在类中。

面向对象编程的优点

他们从外观看上去,只有代码组织方式不同的感觉,面向对象的价值在哪里?

适合大规模复杂项目面向对象编程更加适合大规模复杂项目开发。

对于大规模复杂程序的开发,程序的处理流程并非简单的一条流水线,而是错综复杂的网状结构。

面向对象编程之前会先面向对象分析和面向设计,分析如何给业务建模,如何将需求翻译为类,如何给类之间建立交互关系,而完成这些工作完全不需要考虑错综复杂的处理流程。

当我们有了类的设计之后,然后再像搭积木一样,按照处理流程,将类组装起来形成整个程序。这种开发模式、思考问题的方式,能让我们在应对复杂程序开发的时候,思路更加清晰。

模块化组织代码面向对象编程提供了一种更加清晰的、更加模块化的代码组织方式--类。

比如,我们开发一个电商交易系统,业务逻辑复杂,代码量很大,可能要定义数百个函数、数百个数据结构,那如何分门别类地组织这些函数和数据结构,才能不至于看起来比较凌乱呢?

类就是一种非常好的组织这些函数和数据结构的方式,是一种将代码模块化的有效手段。

当然,像 C 语言这种面向过程的编程语言,我们也可以按照功能的不同,把函数和数据结构放到不同的文件里,以达到给函数和数据结构分类的目的,照样可以实现代码的模块化。是这样!

只不过面向对象编程本身提供了类的概念,强制你做这件事情,而面向过程编程并不强求。不强求的话,每个程序员就可以放飞自我自由发挥了,试想下如果有上百万行代码的项目,这结果......有时候太多的自由反而不利于建议标准,这也算是面向对象编程相对于面向过程编程的一个微创新吧。

封装、抽象、继承、多态面向对象编程相比面向过程编程,具有更加丰富的特性(封装、抽象、继承、多态)。利用这些特性编写出来的代码,更加易扩展、易复用、易维护。

这一点可能有点抽象,下面我会依次展开。

面向对象语言丰富面向对象编程语言丰富,语言越高级使用方式就越低级,程序员的学习门槛越低,面向对象编程语言比起面向过程编程语言,更加人性化、更加高级、更加智能。

这一点,从我上图中,面向对象编程代表语言:Java、C++、Go、Python、C#、Ruby、JavaScript、Objective-C、Scala、PHP、Perl 就可看出。

指导落地案例丰富无数成熟落地项目证明,面向对象编程是可以指导落地、并且能抗住长期需求迭代的编程方式。封装、继承、多态、抽象

封装、继承、多态、抽象,这四个就是面向对象编程的四驾马车,是指导面向对象语言发展的指导思想,是语言无关的,大多数的高级语言几乎都实现了这四个特性,就算没有直接写语法支持,也可以间接支持。

我想强调一点的是,这四驾马车的语言产品,虽然不同语言语法不同,但是这四个特性是都可以实现的。先有面向对象编程的四大特性,再有语言产品。

封装

封装也叫作信息隐藏或者数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或者叫函数)来访问内部信息或者数据。

对于封装这个特性,我们需要编程语言本身提供一定的语法机制来支持。这个语法机制就是访问权限控制。private、public 等关键字就是 Java 语言中的访问权限控制语法。

封装价值 自由也意味着不可控,如果属性可以随意被以各种奇葩的方式修改,而且修改逻辑可能散落在代码中的各个角落,势必影响代码的可读性、可维护性。不满足设计模式提倡的高内聚。

抽象

封装主要讲的是如何隐藏信息、保护数据,而抽象讲的是如何隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。

就好比,客户买电子产品,只需要提供一个使用说明书即可,没必要将电子产品具体的实现原理,底层知识告知,用户只关心功能使用,有时候知道的越多对使用者反而是一种负担。

在面向对象编程中,我们常借助编程语言提供的接口类(比如 Java 中的 interface 关键字语法)或者抽象类(比如 Java 中的 abstract 关键字语法)这两种语法机制,来实现抽象这一特性。

ps:抽象有时候会被排除在面向对象的四大特性之外,是因为抽象这个概念是一个非常通用的设计思想,并不单单用在面向对象编程中,也可以用来指导架构设计等。而且这个特性也并不需要编程语言提供特殊的语法机制来支持,只需要提供“函数”这一非常基础的语法机制,就可以实现抽象特性、所以,它没有很强的“特异性”,有时候并不被看作面向对象编程的特性之一。

抽象价值很多设计原则都体现了抽象这种设计思想,比如基于接口而非实现编程、开闭原则(对扩展开放、对修改关闭)、代码解耦(降低代码的耦合性)等。用好了这些设计原则,我们的代码会变得非常灵活。

继承

继承最大的一个好处就是代码复用。假如两个类有一些相同的属性和方法,我们就可以将这些相同的部分,抽取到父类中,让两个子类继承父类。这样,两个子类就可以重用父类中的代码,避免代码重复写多遍。

不过,过度使用继承,继承层次过深过复杂,就会导致代码可读性、可维护性变差。为了了解一个类的功能,我们不仅需要查看这个类的代码,还需要按照继承关系一层一层地往上查看“父类、父类的父类……”的代码。因此你可能听说过”多用组合少用继承“ 这种说法,有机会再展开说说。

多态

「多态是指子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。」 多态这种特性也需要编程语言提供特殊的语法机制来实现,比如继承、接口类。多态可以提高代码的扩展性和复用性,是很多设计模式、设计原则、编程技巧的代码实现基础。

利用“继承加方法重写”方式实现多态

个人觉得这个是最难理解的,我学生期间对于这个理解一直是半懂状态。网上最多的栗子是通过继承的方式,子类继承父类,重写父类方法,然后父类引用指向子类对象,执行时实际上运行子类的实现,达到一个同一个方法在执行时候表现出来不同的形态的效果,巧妙的将子类替换成了父类。这种实现需要语言满足三个特性:1. 继承 2. 重写 3.父类引用指向子类对象。这个例子我不写实现,网上几乎都是这种例子

利用"接口类"来实现多态特性

public interface Iterator {  boolean hasNext();  String next();  String remove();}public class Array implements Iterator {  private String[] data;    public boolean hasNext() { ... }  public String next() { ... }  public String remove() { ... }  //...省略其他方法...}public class LinkedList implements Iterator {  private LinkedListNode head;    public boolean hasNext() { ... }  public String next() { ... }  public String remove() { ... }  //...省略其他方法... }public class Demo {  private static void print(Iterator iterator) {    while (iterator.hasNext()) {      System.out.println(iterator.next());    }  }    public static void main(String[] args) {    Iterator arrayIterator = new Array();    print(arrayIterator);        Iterator linkedListIterator = new LinkedList();    print(linkedListIterator);  }}

我们利用多态的特性,仅用一个 print() 函数就可以实现遍历打印不同类型(Array、LinkedList)集合的数据。当再增加一种要遍历打印的类型的时候,比如 HashMap,我们只需让 HashMap 实现 Iterator 接口,重新实现自己的 hasNext()、next() 等方法就可以了,完全不需要改动 print() 函数的代码。所以说,多态提高了代码的可扩展性

如果我们不使用多态特性,我们就无法将不同的集合类型(Array、LinkedList)传递给相同的函数(print(Iterator iterator) 函数)。我们需要针对每种要遍历打印的集合,分别实现不同的 print() 函数,比如针对 Array,我们要实现 print(Array array) 函数,针对 LinkedList,我们要实现 print(LinkedList linkedList) 函数。而利用多态特性,我们只需要实现一个 print() 函数的打印逻辑,就能应对各种集合数据的打印操作,这显然提高了代码的复用性

除此之外,多态也是很多设计模式、设计原则、编程技巧的代码实现基础,比如策略模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等等。

总之,封装隐藏属性,抽象隐藏方法,继承支持复用,多态支持扩展

写这个的原因

评判代码好坏的标准有哪些? 除了少些bug,还有灵活性、可扩展性、可维护性、可读性、可理解性、易修改性、可复用、可测试性、模块化、高内聚低耦合、高效、高性能、安全性、兼容性、易用性、整洁、清晰、简单、健壮性、鲁棒性......大多数人还停留在"代码和我,有一个能跑就行"的阶段......

最近的工作,使得我阅读了大量代码,发现有些代码设计的真实精妙,自己肯定写不来这种代码,让我设计,我应该怎么写?然后脑海中又浮现出一句主管经常说的话"不要在这里生产垃圾",内心甚是惶恐,于是学习设计模式的优先级从学习列表排期中跃然成为第一位。

理解面向对象是理解设计模式的第一位,毕竟设计模式都是前辈们在面向对象编程中总结出来的设计方式,在无数代码中提炼出来的精华。因此今年自己的输出将会侧重在设计模式相关的内容,打算写一个系列,欢迎持续关注。

参考:王争老师设计模式之美,《设计模式之禅》

往期精彩推荐:

开发必会的测试知识,Junit+Mock+Assert+DevOps

Kong 优雅实现微服务网关鉴权,登录场景落地实战篇

一文弄懂什么是DevOps

标签: #php面向对象程序设计的优缺点