设计模式(二):面向对象及其特性分析

面向对象

根据大纲,首先我们来学习一种代码设计与编写的风格,即面向对象。

面向对象编程和面向对象编程语言

  • 面向对象编程是一种编程范式,通俗来说,就是将 代码的组织单元改成类和对象,并将 封装、继承、抽象、多态 作为代码设计和编写的基石。
  • 面向对象编程语言代表了能简易实现面向对象编程范式的语言,其如果能够用现成的语法机制来实现范式,就是面向对象编程语言。

从上述的定义中,我们可以看出,面向对象编程不一定非要用面向对象编程语言来实现,同时,当我们使用面向对象编程语言来编写代码时,可能写出来的却反而是面向过程的代码。

面向对象分析和面向对象设计分别是什么

既然面向对象编程重点是类和对象的实现,那么针对分析和设计其实就是类和对象该如何设计。

分析讲的就是做什么,而设计讲的就是怎么做。通过这两个阶段产出类的设计,包括该拆出什么类、每个类有哪些属性和方法、类和类之间如何交互等。

封装

定义:封装的定义是隐藏类中的信息。通过暴露有限的接口,来保护一些数据,只提供一些开放的接口来完成指定的业务。

实现方式:在 Java 代码的实现通过 public、private 等关键词来实现。通过这些关键字来确定访问权限,从而隐藏对应字段。

意义:

  • 安全、可控,因为我们指定了哪些属性是可暴露给外部,哪些是只能按照指定方式来修改的,因此更加安全可控;
  • 好理解,因为在代码的实现里封装了多数细节,只暴露一些开放的接口,因此不需要调用者太了解全部就可以使用了。

抽象

定义:抽象的定义和封装类似,封装封的是类内的属性字段,抽象抽的是行为的具体实现方式。

实现方式:在 Java 代码里通过 interface 或 abstract class 来实现。但该特性并不是一个面向对象特有特性,只要语言里有函数的概念,就可以通过多种方式来实现抽象特性。

意义:

  • 因为只暴露方法的定义,因此对使用方来说,不需要关注具体的实现,只面向定义使用即可,内部如何实现以及后续如何更改,只要保证定义不变,使用方就不需要更改;

为了实现上述意义,因此在设计方法定义时,尽量保证方法的定义和具体实现无关,来保证面对需求的修改时可以从容修改。

继承

定义:继承好理解了,其实现的是 is-a 的语义,举个例子来说,设计父类是哺乳动物的话,那么子类就可以是猫、狗、人等,这些子类都属于父类的一种。

实现方式:在 Java 代码里继承是通过 extend 关键字来实现的。

意义:继承最重要的就是解决代码复用的问题,即子类复用父类的属性和方法,避免重复的代码编写多次。

带来的问题:

  • 多继承问题,在 Java 里是没有多继承的,因为多继承会出现菱形问题,导致语义不清,因此官方不允许使用多继承,即一个子类只可以继承一个父类。
  • 继承层次过深,如果继承的层级结构比较复杂,那么在想要知道一个类中的某个具体行为实现,可能就需要看很多层级,从而影响代码的可读性和可维护性。

image.png

在 A 中定义了一个方法 test(),类 B 和 类 C 都继承了A,并对 test() 进行了重写,此时有个类 D 同时继承类 B 和类 C,那么类 D 在调用 test() 方法的时候,该调用哪一个呢?这就是菱形问题带来的语义混乱。

组合和继承:
针对上述所述的问题,因此推荐使用组合的形式来代替继承,具体的实现方式的话,在后面的文章中再详细描述。

多态

定义:简单来说,就是父类指针可以指向子类对象,从而在代码实际运行过程中,根据具体是哪一个对象,来决定调用哪个对象的方法。

实现方式:文字不好解释,直接代码来解释。

// 首先定义一个接口
public interface ImageStore {
    void sayHello();
}

// 定义一个类来实现接口,并重写其中的方法
public class AliyunImageStore implements ImageStore {
    @Override
    public void sayHello() {
        System.out.println("AliyunImageStore say Hello");
    }
}

// 再定义一个类来实现接口,并重写其中的方法
public class TencentImageStore implements ImageStore {
    @Override
    public void sayHello() {
        System.out.println("TencentImageStore say Hello");
    }
}

// 用接口来指向具体子类,从而调用子类的方法
public class Demo {
    public static void main(String[] args) {
        ImageStore aliyun = new AliyunImageStore();
        aliyun.sayHello();

        ImageStore tencent = new TencentImageStore();
        tencent.sayHello();
    }
}

image.png

意义:
提高代码的可拓展性,通过父类指针来操作数据,保证在代码中实现一个逻辑,可以复用多次。

公众号截图

文章在公众号「iceWang」第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!