笔者作为一个程序员新人,在之前的工作中往往仅考虑了功能的实现与问题的修复,而缺失了对整体架构的一个思考。

作为一个 Android 开发工程师,平时工作是利用面向对象的编程语言 Java 进行项目开发,同时在近日刚完成了一次项目重构,对此我有了架构上面的一些思考,其中最基础的就是对「面向对象」的思考。

什么是面向对象

面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的程序编程典范,同时也是一种程序开发的抽象方针。面向对象最基础的概念就是「类」和「对象」,并将其作为程序的基本单元,并拥有「封装」、「抽象」、「继承」、「多态」的特性。

  • 类(Class):定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。
  • 对象:是类的实例。

面向对象编程能够更方便地实现一些通过面向对象分析设计的需求,可以很方便将某些现实事物转化成更简洁的代码去实现,最简单的例子就是我们平时写的 Java Bean,能够将某个事物的特性写进某个类或对象里。

Common Lisp、Python、C++、Objective-C、Smalltalk、Delphi、Java、Swift、C#、Perl、Ruby 与 PHP 都是面向对象编程语言。

面向对象特性

上文提到,面向对象编程的关键就是「封装」、「抽象」、「继承」、「多态」,这四大特性在面向对象编程中起着非常关键的作用。以下通过介绍每一个特性来理解其在面向对象编程中的作用。

封装

以下是 Java 中经典的体现封装特性的代码:

public class Person{
    private String name;
    private int age;
    
    public int getAge(){
      return age;
    }
    
    public String getName(){
      return name;
    }
    
    public void setAge(int age){
      this.age = age;
    }
    
    public void setName(String name){
      this.name = name;
    }
}

这里用 publicprivate 体现了 Java 语言的一个体现封装特性的语法机制:访问权限控制。通过控制访问权限,将一些属性封装起来,让外部仅可以通过对外暴露的方法去对一些属性进行修改。在现实中更多是为了控制某些属性按所期待的方式进行修改,而不是直接修改属性进行修改,相信在工作中也能看到很多相关的例子,为了保护数据和隐藏信息而作一些限制。

当然这看起来好像直接访问和修改属性能够使代码变得更灵活,但是随着代码量的增长直接访问和修改属性会使代码的可读性和可维护性受到了非常大的影响。仅仅暴露有限的方法能够使这个类更加易用,在大多数开发者的工作中会涉及到非常广的团队协作,若要对每个类的每个属性进行熟悉会大大降低工作效率,通过暴露有限的方法且方法命名简洁规范可大大提升工作效率,想必这也是封装特性的一个重要意义吧。

抽象

封装特性是为了通过暴露有限的方法来达到保护数据的作用,而抽象则是对方法的具体实现进行隐藏,让调用者只需要关心方法提供哪些功能。在 Java 中可以通过接口类或抽象类的语法机制来实现这个特性。

广义来说,抽象特性就是通过编程语言的「函数」来表现的,不一定要通过类似于抽象类或接口类来进行表现。比如 C 语言的 malloc() 函数就是一个抽象的表现,开发者并不需要知道里面发生了什么,仅需要知道这个方法调用后能有什么结果。

其实「封装」和「抽象」都是为了处理复杂代码提升效率,毕竟人一时间能承受的范围是有限的,因此通过以上特性出来了很多开源库为我们日常开发“提速”,仅关注功能不关注实现,为大脑减轻负担,提高代码的扩展性、可维护性,降低复杂度

同时,抽象特性在代码设计中起到非常大的作用,比如基于接口而非实现编程、开闭原则、代码解耦都用到了抽象特性。

继承

继承是用来表示类之间的 is-a 关系的,比如猴子是动物,在 Java 中通过 extends 关键字来实现继承。

继承最大的作用就是用来实现更好的代码复用,如果两个类有相同的一部分属性,即可抽取一个父类用于放置相同属性,子类继承于它,这样两个类就可以减少重复代码的编写。当然如果在编写代码的过程中嵌套太多层的继承关系也会适得其反,复杂的代码层级关系使得代码可读性及可维护性下降。所以,只有合适的继承关系使用可解决代码复用问题,提升可维护性

多态

多态是指子类可以替换父类,在运行时调用子类的实现。以下是 Java 语言实现多态的一种方式。

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断
        if (a instanceof Cat)  {  // 猫做的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
}
 
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

多态的实现需要编程语言提供特殊的语法机制,比如继承加方法重写、接口类。多态用于提高代码的可扩展性和复用性,通过多态使代码变得更加灵活。菜鸟教程给出了一个例子:

现实中,比如我们按下 F1 键这个动作:

  • 如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
  • 如果当前在 Word 下弹出的就是 Word 帮助;
  • 在 Windows 下弹出的就是 Windows 帮助和支持。

同一个事件发生在不同的对象上会产生不同的结果。

故有了多态特性,使得此类行为在代码中的控制更加简单快捷。同时,多态也是很多设计模式/原则实现的基础,比如策略模式、基于接口而非实现编程、依赖倒置原则、里氏替换原则等待。

面向过程与面向对象

与面向对象编程相比较的,往往就是面向过程编程。面向过程编程也是一种编程范式或编程风格,以过程(可理解为方法)作为组织代码的基本单元,以数据(可理解为属性)与方法相分离为最主要的特点,通过组织执行方法的顺序来完成一个功能。

最典型的面向过程编程语言即为 C 语言。与面向对象不同的是面向过程不是以「类」和「对象」作为组成程序的基本单元,同时方法和数据结构定义是分开的,而面向对象方法和数据结构都包含在类里绑定在了一起。

面向对象编程相比面向过程,无非就是其封装、继承、抽象、多态特性,使得代码的可读性、可维护性、可扩展性增强,更适合用于有大型复杂业务的系统开发,提升协作效率。相信大多数开发者都不是独立开发一个系统,故通过面向对象编程语言利用这些特性做好架构设计,能为工作效率提升一个台阶。

总结

「面向对象」看似是一个很基础的概念,实则很多人并没有真的理解它。说简单也简单,说难也难,归根到底就是「封装」、「抽象」、「继承」、「多态」这四个特性。其实,所有设计模式、设计原则都是基于「面向对象」而设的,所以理解好「面向对象」这个概念能够对工作有更大的帮助。

文章若有纰漏,请留言或电子邮箱联系我:me@york1996.com。文章部分内容引用自:[维基百科 - 面向对象程序设计](

标签: 面向对象, 设计模式

添加新评论