面向对象

和面向过程的辨析

  • 面向过程编程(Procedure Oriented Programming)

    核心思想:面向过程的核心是以过程或者说函数作为程序设计的基本单元,强调的是解决问题的步骤和顺序。

    特点

    • 程序结构清晰,通常体现为函数的组合和调用,关注的是如何一步步地执行操作。
    • 数据和处理数据的函数通常是分开的,通过函数参数传递数据。
    • 优点在于简洁明了,执行效率较高,适用于小型、逻辑相对简单的项目。
    • 缺点在于随着程序规模扩大,各部分之间的耦合度会增加,难以复用和维护,不便于管理复杂的系统。
  • 面向对象编程(Object Oriented Programming)

    核心思想:面向对象编程则是以“对象”为核心,将数据(属性)和操作数据的行为(方法)封装在一起,形成具有独立职责的对象。程序设计围绕着对象及其交互来进行

    特点:

    • 强调概念抽象,封装、继承和多态这三大特性。
    • 封装使得数据隐藏,对外提供接口,增强了安全性和减少耦合度。
    • 继承允许子类继承父类的属性和方法,减少代码重复并构建出层次化的类结构。
    • 多态使得同一接口可以有不同的表现形式,增加了代码的灵活性和扩展性。
    • 优点在于提高了代码的复用性、可维护性和可扩展性,更适应大型软件系统的开发。
    • 缺点包括学习成本相对较高,且过度设计可能导致性能损失(尤其是在大量创建对象时)

总结来说,面向过程着重于算法和逻辑控制的流程,而面向对象则更关注现实世界的实体模型和其行为,强调的是利用对象间的交互来解决问题。在实际开发中,两者并不是绝对对立,现代编程实践中往往结合两者的优势,灵活运用在不同的场景中

一个案例

需求:

要设计一个简单的银行账户管理系统,需求如下:创建账户、存款、取款、查询余额

面向过程编程

以方法,流程,全局变量来实现解决问题,实现目的

import java.util.HashMap;
import java.util.Map;

public class BankProcess {
    // 全局账户字典用于存储账户信息
    static Map<String, Double> accounts = new HashMap<>();

    public static void main(String[] args) {
        createAccount("A001", 1000.0);
        deposit("A001", 500.0);
        System.out.println("Balance after deposit: " + checkBalance("A001"));
        withdraw("A001", 200.0);
        System.out.println("Balance after withdrawal: " + checkBalance("A001"));
    }

    // 创建账户
    public static void createAccount(String accountId, double initialBalance) {
        accounts.put(accountId, initialBalance);
    }

    // 存款操作
    public static void deposit(String accountId, double amount) {
        if (accounts.containsKey(accountId)) {
            accounts.put(accountId, accounts.get(accountId) + amount);
        }
    }

    // 取款操作
    public static void withdraw(String accountId, double amount) {
        if (accounts.containsKey(accountId) && accounts.get(accountId) >= amount) {
            accounts.put(accountId, accounts.get(accountId) - amount);
        }
    }

    // 查询余额
    public static double checkBalance(String accountId) {
        return accounts.getOrDefault(accountId, 0.0);
    }
}

面向对象编程

以对象,对象的属性,对象的行为(方法)来解决问题,实现目的

import java.util.HashMap;
import java.util.Map;

class BankAccount {
    private String accountId;
    private double balance;

    public BankAccount(String accountId, double initialBalance) {
        this.accountId = accountId;
        this.balance = initialBalance;
    }

    public static BankAccount getAccount(String accountId) {
        return allAccounts.get(accountId);
    }

    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
        }
    }

    public double checkBalance() {
        return balance;
    }

}

@Test
public void test(){
    List<BankAccount> allAccounts = new ArrayList<>();
    BankAccount account = new BankAccount("A001", 1000.0);
    allAcounts.add()
    account.deposit(500.0);
    System.out.println("Balance after deposit: " + account.checkBalance());
    account.withdraw(200.0);
    System.out.println("Balance after withdrawal: " + account.checkBalance());
}

面向对象的三大特性

封装

将数据(属性)和操作数据的方法(函数或者方法)绑定在一起,作为一个整体(对象)进行考虑,通过封装,可以隐藏对象的内部细节,仅对外提供公共访问方式,从而提高代码的复用性和安全性。在程序设计中,对象的状态通常由其属性来体现,而行为则通过方法来执行

继承

继承是一种创建新类的方式,新类可以从已有的类中派生出来,继承父类的属性和方法,同时也可以扩展新的属性和方法.这样可以提高代码的重用性,使得类和类之间产生了层次结构,便于系统的维护和扩展

多态

多态,指为不同数据类型的实体提供统一的接口,以Java语言为例,多态有以下要素

  • 继承,多个子类继承/实现了一个父类
  • 重写,多个子类重写了父类的同名方法,有不同实现
  • 父类引用指向子类对象(默认调用子类的重写方法)
//1.继承,父类
public class Animal {
  public void shout();
}

//1.继承,一个子类
@Slf4j
public class Dog extends Animal {
    
  //2.重写了父类的方法  
  @Override
  public void shout() {
    log.info("wanwan");
  }
}

@Slf4j
public class Cat extends Animal {
  @Override
  public void shout() {
    log.info("mimi");
  }
}

@Test
public void test() {
    
  Animal ani = new Dog();//3.父类引用指向子类对象
  ani.shout();//3.调用子类dog重写后的方法 wanwan
    
}

上述代码展示了多态,支持多态就意味着,所有需要具体子类的地方都可以用父类笼统概括,最直观的莫过于,同一个方法用父类做形参,可以传入不同子类实现不同的功能,降低了耦合度,提高了扩展性

面向对象的五大原则

单一职责原则(Single Responsibility Principle)

一个类或者模块应该有且只有一个改变的理由,即一个类应该只有一个职责,当且仅当它负责的功能发生变化时才需要修改

开放封闭原则(Open/Close Principle)

类模块应该是可扩展的,但不可修改,在不修改已有代码的情况下,能够通过扩展模块的行为来添加新的功能

为什么叫开放封闭原则?

对扩展开放,对修改关闭

里氏替换原则(Liskov Substitution Principle)

子类型必须能够替换掉它们的基类型。这意味着在继承体系中,子类应当可以在任何需要基类出现的地方无差别地替代基类,而且不会破坏原有的正确性

为什么叫里氏替换原则?

里斯科夫(Liskov)女士在 1987 年的“面向对象技术的高峰会议”(OOPSLA)上发表的一篇文章《数据抽象和层次》(Data Abstraction and Hierarchy)里提出来:”继承必须确保超类所拥有的性质在子类中仍然成立”

接口隔离原则(Interface Segregation Principle)

接口隔离原则建立在使用接口的情况下,客户端不应该被迫使用对其无用的方法或者功能,一个类对另一个类的依赖应该建立在最小的接口上

实践起来就是接口不应该过于臃肿和冗余,依赖一个臃肿冗余的接口,不如把这个接口拆成多个,来按需依赖

依赖倒置原则(Dependence Inversion Principle)

  • 高层模块(如业务逻辑层)不应该依赖于底层模块(如数据访问层),二者都应该依赖其抽象
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒置的核心是”面向接口编程”
  • 抽象对比细节,要稳定,以抽象为基础搭建的框架比细节的框架要稳定的多

依赖倒置原则的依赖倒置体现在哪里?

在传统的自底向上设计中,高层模块(如业务逻辑层)会直接依赖底层模块(如数据访问层)形成一种具体依赖具体的依赖关系

个人理解的倒置,应该和高层,底层没什么关系,是一种理念上的倒置,即由依赖具体转变为依赖抽象

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。