You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

面向对象编程:无法修改父类时如何实现OpenTicket?

面向对象设计:无修改权限下实现OpenTicket的最优方案

问题背景

现有一个不可修改的Java Ticket 类实现车票功能,代码如下:

public class Ticket {
    private LocalDateTime dateAndTime;
    private String departureCity;
    private String arrivalCity;

    public Ticket(LocalDateTime dateAndTime, String departureCity, String arrivalCity) {
        assert (isDateAndTimeCorrect(dateAndTime));
        assert (isRouteCorrect(departureCity, arrivalCity));
        /** ... **/
    }

    public double getPrice() { /** ... **/ }

    @Override
    public String toString() { /** ... **/ }

    public String getDepartureCity() { /** ... **/ }

    public String getArrivalCity() { /** ... **/ }

    public LocalDateTime getDateAndTime() { /** ... **/ }

    public boolean isDateAndTimeCorrect(LocalDateTime dateAndTime) {
        return dateAndTime != null && dateAndTime.isAfter(LocalDateTime.now());
    }

    public boolean isRouteCorrect(String departureCity, String arrivalCity) {
        return departureCity != null && arrivalCity != null && !departureCity.isEmpty() && !arrivalCity.isEmpty()
                && !departureCity.equals(arrivalCity);
    }
}

需要在不修改原Ticket类的前提下,新增支持未确定日期时间的OpenTicket类型,现有两种方案待评估:

  • 子类调用父类构造器时传入当前时间之后的某个未来日期
  • 传入null并设法规避断言

方案对比与结论

方案2(传入null并规避断言):绝对不可取

  • 断言是原类用来验证前置条件的设计,规避断言本质上直接破坏了Ticket的设计契约。原类明确要求dateAndTime必须是非空且在未来的合法值,强行传入null会让Ticket内部状态完全不符合预期,后续调用getDateAndTime()等方法大概率触发NullPointerException,或让依赖Ticket状态的业务逻辑出现异常。
  • 这种行为违背里氏替换原则:OpenTicket作为子类,应当能安全替换父类使用,但非法状态的子类根本无法保证父类所有方法正常工作。
  • 规避断言的手段(比如全局关闭断言开关)极不可靠:开关是JVM级别的,无法仅针对OpenTicket生效,会导致其他依赖断言的代码也失去前置验证,引入更多潜在bug。

方案1(传入合法未来日期):当前约束下的最优选择

这个方案严格遵守父类的设计契约,保证OpenTicket的父类部分处于合法状态,符合里氏替换原则,不会破坏原类逻辑。但需要补充子类的封装逻辑,明确“占位日期”的业务含义:

public class OpenTicket extends Ticket {
    private boolean isDateUnconfirmed = true;

    public OpenTicket(String departureCity, String arrivalCity) {
        // 传入一个较远的未来日期作为占位,比如10年后的同一时刻
        super(LocalDateTime.now().plusYears(10), departureCity, arrivalCity);
    }

    // 对外暴露未确定日期的标识
    public boolean isDateUnconfirmed() {
        return isDateUnconfirmed;
    }

    // 重写getDateAndTime,明确告知调用者当前日期未确认
    @Override
    public LocalDateTime getDateAndTime() {
        if (isDateUnconfirmed) {
            throw new IllegalStateException("该未确认日期的车票暂无确定出行时间");
        }
        return super.getDateAndTime();
    }

    // 提供确认日期的方法,验证并更新状态
    public void confirmDate(LocalDateTime confirmedDate) {
        if (!super.isDateAndTimeCorrect(confirmedDate)) {
            throw new IllegalArgumentException("确认日期必须是未来的时间");
        }
        isDateUnconfirmed = false;
        // 注:如果父类没有提供修改dateAndTime的入口,可根据业务调整逻辑,比如后续返回确认后的日期
    }
}

核心思路是在遵守父类契约的基础上,通过子类扩展实现“未确定日期”的业务特性,既保证了与原系统的兼容性,又符合面向对象的封装、继承原则。

额外优化建议:优先考虑组合而非继承

如果业务允许调整依赖结构,OpenTicket可以不继承Ticket,而是通过组合方式持有Ticket实例,确认日期时再创建合法的Ticket对象。这种方式更符合组合优于继承的原则,彻底避免继承带来的契约冲突:

public class OpenTicket {
    private String departureCity;
    private String arrivalCity;
    private Ticket confirmedTicket;

    public OpenTicket(String departureCity, String arrivalCity) {
        // 复用父类的路线合法性验证逻辑
        if (!new Ticket(null, departureCity, arrivalCity).isRouteCorrect(departureCity, arrivalCity)) {
            throw new IllegalArgumentException("出发地与到达地不合法");
        }
        this.departureCity = departureCity;
        this.arrivalCity = arrivalCity;
    }

    public boolean isConfirmed() {
        return confirmedTicket != null;
    }

    public void confirmDate(LocalDateTime dateTime) {
        this.confirmedTicket = new Ticket(dateTime, departureCity, arrivalCity);
    }

    public Ticket getConfirmedTicket() {
        if (!isConfirmed()) {
            throw new IllegalStateException("车票尚未确认日期");
        }
        return confirmedTicket;
    }

    // 提供必要的属性访问方法
    public String getDepartureCity() {
        return departureCity;
    }

    public String getArrivalCity() {
        return arrivalCity;
    }
}

这种方案的缺点是需要适配原系统对Ticket类型的依赖,但从面向对象设计的健壮性来看,这是更优的长期方案。

内容的提问来源于stack exchange,提问作者Cardstdani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 10:43:10