CodeGym /课程 /JAVA 25 SELF /在真实场景中构建抽象的示例

在真实场景中构建抽象的示例

JAVA 25 SELF
第 19 级 , 课程 3
可用

1. 真实应用中的抽象:为什么需要它

在之前的讲座中,我们已经认识了抽象的概念,并看过了一些简单示例。现在看看这种方法在更真实的任务中如何发挥作用。 在真实项目中,几乎总要处理不同但相似的对象。例如,不同的支付方式、不同类型的交通工具、图形编辑器中的不同图形。如果不应用抽象,代码很快就会变成一堆“if-else”和复制粘贴。使用抽象——一切更规范、优雅,并且最重要的是,便于演进和维护。

抽象可以:

  • 隐藏实现细节:通过统一接口与对象交互,而无需关心其内部实现。
  • 避免代码重复:将共同行为与字段提取到基类。
  • 便于扩展系统:添加新类型的对象无需重写旧代码。
  • 让代码更灵活:可以在不改动其余系统的情况下替换一种实现为另一种实现。

让我们从不同领域看几个示例。

2. 示例 1:支付系统

任务说明

假设你在为一个网店编写模块。你的任务是实现多种支付方式的处理:银行卡、PayPal、加密货币。它们都需要“处理支付”,但细节各不相同。

抽象:类 Payment

public abstract class Payment {
    protected double amount;

    public Payment(double amount) {
        this.amount = amount;
    }

    // 抽象方法:具体如何处理支付——由子类决定
    public abstract void process();

    // 所有支付的通用方法
    public void printAmount() {
        System.out.println("Payment amount: " + amount + " CNY");
    }
}

具体实现

public class CreditCardPayment extends Payment {
    private String cardNumber;

    public CreditCardPayment(double amount, String cardNumber) {
        super(amount);
        this.cardNumber = cardNumber;
    }

    @Override
    public void process() {
        System.out.println("处理银行卡支付: " + cardNumber);
        // 这里可以是与银行的集成 :)
    }
}
public class PaypalPayment extends Payment {
    private String email;

    public PaypalPayment(double amount, String email) {
        super(amount);
        this.email = email;
    }

    @Override
    public void process() {
        System.out.println("处理 PayPal 支付,账户: " + email);
        // 这里——调用 PayPal API
    }
}
public class CryptoPayment extends Payment {
    private String walletAddress;

    public CryptoPayment(double amount, String walletAddress) {
        super(amount);
        this.walletAddress = walletAddress;
    }

    @Override
    public void process() {
        System.out.println("处理加密货币支付,钱包地址: " + walletAddress);
        // 这里可以是区块链魔法
    }
}

抽象的使用

import java.util.*;

public class PaymentDemo {
    public static void main(String[] args) {
        List<Payment> payments = new ArrayList<>();
        payments.add(new CreditCardPayment(1500.0, "1234 5678 9012 3456"));
        payments.add(new PaypalPayment(500.0, "user@example.com"));
        payments.add(new CryptoPayment(0.05, "0xABCD..."));

        for (Payment payment : payments) {
            payment.printAmount();
            payment.process();
            System.out.println("---");
        }
    }
}

运行结果:

Payment amount: 1500.0 CNY
处理银行卡支付: 1234 5678 9012 3456
---
Payment amount: 500.0 CNY
处理 PayPal 支付,账户: user@example.com
---
Payment amount: 0.05 CNY
处理加密货币支付,钱包地址: 0xABCD...
---

优点:

  • 可以在不修改旧代码的情况下添加新的支付方式(例如 Apple Pay)。
  • 处理支付的代码不依赖于具体类型。
  • 通用逻辑(例如通过 printAmount() 打印金额)集中在一个地方实现。

3. 示例 2:交通工具

任务说明

在游戏或模拟器中,你有多种交通工具:汽车、自行车、火车。它们都可以“移动”,但方式不同。有些需要加油,另一些则不需要。

抽象:类 Transport

public abstract class Transport {
    protected String name;

    public Transport(String name) {
        this.name = name;
    }

    public abstract void move();

    // 并非所有交通工具都需要加油,默认——不需要
    public void fuelUp() {
        System.out.println(name + ": 不需要加油。");
    }
}

具体实现

public class Car extends Transport {
    public Car(String name) {
        super(name);
    }

    @Override
    public void move() {
        System.out.println(name + " 在道路上行驶。");
    }

    @Override
    public void fuelUp() {
        System.out.println(name + ": 加注汽油。");
    }
}
public class Bicycle extends Transport {
    public Bicycle(String name) {
        super(name);
    }

    @Override
    public void move() {
        System.out.println(name + " 踩踏板前进。");
    }
    // 不重写 fuelUp —— 自行车不需要加油
}
public class Train extends Transport {
    public Train(String name) {
        super(name);
    }

    @Override
    public void move() {
        System.out.println(name + " 在轨道上飞驰。");
    }

    @Override
    public void fuelUp() {
        System.out.println(name + ": 加注柴油或使用电力。");
    }
}

抽象的使用

import java.util.*;

public class TransportDemo {
    public static void main(String[] args) {
        List<Transport> vehicles = Arrays.asList(
            new Car("Toyota"),
            new Bicycle("Stels"),
            new Train("Sapsan")
        );

        for (Transport t : vehicles) {
            t.move();
            t.fuelUp();
            System.out.println("---");
        }
    }
}

运行结果:

Toyota 在道路上行驶。
Toyota: 加注汽油。
---
Stels 踩踏板前进。
Stels: 不需要加油。
---
Sapsan 在轨道上飞驰。
Sapsan: 加注柴油或使用电力。
---

优点:

  • 可以以相同方式处理任何交通工具,无需检查其类型。
  • 易于添加新的交通工具(例如电动滑板车)。

4. 示例 3:图形编辑器

任务说明

你在编写一个迷你图形编辑器。其中有直线、椭圆、多边形——它们都是“图形”,可以被绘制并调整大小,而且每种图形会以自己的方式实现这些操作。

抽象:类 Figure

public abstract class Figure {
    protected String color = "black";

    public abstract void draw();

    public abstract void resize(double factor);

    public void setColor(String color) {
        this.color = color;
    }
}

具体实现

public class Line extends Figure {
    private double length;

    public Line(double length) {
        this.length = length;
    }

    @Override
    public void draw() {
        System.out.println("绘制长度为 " + length + " 的直线,颜色为 " + color);
    }

    @Override
    public void resize(double factor) {
        length *= factor;
        System.out.println("直线的新长度: " + length);
    }
}
public class Ellipse extends Figure {
    private double a, b;

    public Ellipse(double a, double b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public void draw() {
        System.out.println("绘制轴为 " + a + " 和 " + b + " 的椭圆,颜色为 " + color);
    }

    @Override
    public void resize(double factor) {
        a *= factor;
        b *= factor;
        System.out.println("椭圆的新尺寸: " + a + " x " + b);
    }
}
public class Polygon extends Figure {
    private int sides;

    public Polygon(int sides) {
        this.sides = sides;
    }

    @Override
    public void draw() {
        System.out.println("绘制具有 " + sides + " 条边的多边形,颜色为 " + color);
    }

    @Override
    public void resize(double factor) {
        System.out.println("将具有 " + sides + " 条边的多边形的尺寸按 " + factor + " 进行缩放");
    }
}

抽象的使用

import java.util.*;

public class EditorDemo {
    public static void main(String[] args) {
        List<Figure> figures = new ArrayList<>();
        figures.add(new Line(10));
        figures.add(new Ellipse(5, 3));
        figures.add(new Polygon(6));

        for (Figure f : figures) {
            f.setColor("green");
            f.draw();
            f.resize(2);
            System.out.println("---");
        }
    }
}

运行结果:

绘制长度为 10.0 的直线,颜色为 green
直线的新长度: 20.0
---
绘制轴为 5.0 和 3.0 的椭圆,颜色为 green
椭圆的新尺寸: 10.0 x 6.0
---
绘制具有 6 条边的多边形,颜色为 green
将具有 6 条边的多边形的尺寸按 2.0 进行缩放
---

优点:

  • 可以把所有图形放在同一个列表中并以相同方式处理。
  • 容易添加新的图形(例如星形或心形)。
  • 通用方法(例如通过 setColor() 设置颜色)只需实现一次。

5. 抽象如何帮助简化代码

以上每个示例都有一个共同的模式:

  • 基础抽象类定义契约(对象能做什么)。
  • 具体子类实现细节。
  • 使用抽象编写的代码不依赖对象的具体类型,使系统更加灵活且可扩展。

方法对比表

没有抽象(if-else 有抽象(OOP)
大量基于类型的条件 出现新类型——就要改代码
逻辑重复 逻辑集中在一个地方
难以扩展 添加——很容易
难以测试 易于替换实现

6. 设计抽象时的常见错误

错误 №1:为抽象而抽象。
如果你只有一种对象类型且不计划扩展——就没必要使用抽象类。不要无故让代码复杂化。

错误 №2:抽象过于泛化。
如果基类过于“模糊”,子类可能除了名字外没有共同之处。例如,把所有东西都抽象成“Thing”。这会使维护和理解代码变得困难。

错误 №3:在子类中重复代码。
如果所有子类对某个方法的实现都相同,就应该把该实现上移到基类(让它变为非抽象)。

错误 №4:违反“由泛到专”的原则。
如果抽象类中出现了仅某个子类才需要的细节——说明抽象选错了。

错误 №5:忘记实现抽象方法。
如果没有在子类中实现所有抽象方法,编译器会迫使该类也成为抽象类。有时这会让人意外 :)

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION