Class 设计 - 面向对象编程问题

Class Design - Object Oriented Programming Question

这是在采访中被问到的。

有不同的巴士制造商。每辆巴士都有不同的型号,每个型号只有 2 个变体。所以不同的制造商有不同的型号,只有 2 个变体。面试官让我用 类 设计一个独立的程序。她提到我不应该考虑数据库,也不必编写它们。例如,它可以是一个基于控制台的程序,具有输入和输出。

制造商、型号和变体信息应保存在内存中(硬编码值适用于此独立程序)。她想观察类和我解决问题的方法。

她告诉我要专注于为这个系统实现三个 API 或方法。

第一个是获取有关特定公交车的信息。输入将是制造商名称、型号名称和变体名称。给定这三个值,应该向客户显示有关特定公交车的信息,例如价格、型号、年份等。

第二个 API 将比较两条总线,输出将并排列出功能,可能采用表格格式。输入与第一个 API 相同,即制造商名称、型号名称和两种总线的变体名称。

第三种是按价格(>=价格)搜索公交车,得到满足条件的公交车列表。

她还补充说 APIs 应该是可扩展的,我应该考虑到这个条件来设计解决方案。

我就是这样设计的 类:

class Manufacturer {
    private String name;
    private Set<Model> models;
    // some  more properties related to manufacturer
}

class Model {
    private String name;
    private Integer year;
    private Set<Variant> variants;
    // some more properties related to model
}

class Variant {
    private String name;
    private BigDecimal price;
    // some more properties related to variant
}

class Bus {
    private String manufacturerName;
    private String modelName;
    private String variantName;
    private Integer year;
    private BigDecimal price;
    // some more additional properties as required by client
}

class BusService {

    // The first method
    public Bus getBusInformation(String manufacturerName, String modelName, String variantName) throws Exception {
        Manufacturer manufacturer = findManufacturer(manufacturerName);
        //if(manufacturer == null) throw a valid exception
        Model model = findModel(manufacturer);
        // if(model == null) throw a valid exception
        Variant variant = findVariant(model);
        // if(variant == null) throw a valid exception
        return createBusInformation(manufacturer, model, variant);
    }

}

她强调只有 2 个变体,不会有更多的变体,它应该是可扩展的。在经历了 类 之后,她说她理解我的方法,我不必实施另一个 APIs/methods。我意识到她对我设计它们的方式并不满意。

了解我犯的错误会很有帮助,以便我从中吸取教训。

我会说您对总线 class 的表示受到严重限制,变体、型号、制造商应该是 classes 而不是字符串的硬链接。然后获取每个人的名字。

E.G 从 Bus bus1 的角度 this.variant.GetName() 或者从外界来看。 bus1.GetVariant().name

通过将总线限制为它所持有的片段的字符串,即使在总线内部,您也不得不进行查找 class,这在规模上比简单的内存引用慢得多。

就您的 API 而言(虽然我没有示例),您获取公交车信息的一种方式是有限的。如果总线的构成发生变化(变体变化,引入了新组件 classes),则需要对该函数进行适当的重写,如果其他函数的编写方式类似,那么所有这两个函数都需要。

这需要一些思考,但是可以根据输入动态获取信息的通用方法可以让以后 add/remove 组件更容易。这将是您的面试官在高级技术和语言技能方面最关注的地方。在这里以正确的方式实现泛型、委托等可以使 API 的未来维护变得容易得多。 (对不起,我没有例子)

不过我不会说您的方法一定不好,字符串成员变量可能是唯一的主要问题。

我对您的 3 个要求的解释有点不同(我可能错了)。但听起来整体的愿望是能够对所有模型执行不同的搜索,对吗?

此外,在我看来,所有变体都是模型。我怀疑不同的变体会有不同的选择,但没有任何证据可以证实这一点。如果是这样,变体只是特定模型的子class。但是,如果变体最终具有相同的属性集,那么变体只不过是模型的附加描述符。

无论如何,继续我的怀疑,我会把模型作为中心焦点,然后:

(基础class)

abstract class Model {
    private Manufacturer manufacturer;
    private String name;
    private String variant;
    private Integer year;
    private BigDecimal price;
    // some more properties related to model
}

(制造商型号)

abstract class AlphaModel {
    AlphaModel() {
        this.manufacturer = new Manufacturer() { name = "Alpha" }
    }

    // some more properties related to this manufacturer
}

abstract class BetaModel {
    BetaModel() {
        this.manufacturer = new Manufacturer() { name = "Beta" }
    }

    // some more properties related to this manufacturer
}

(型号变体)

abstract class AlphaBus : AlphaModel {
    AlphaBus() {
        this.name = "Super Bus";
    }
    // some more properties related to this model
}

abstract class BetaTruck : BetaModel {
    BetaTruck() {
        this.name = "Big Truck";
    }
    // some more properties related to this model
}

(实例)

class AlphaBusX : AlphaBus {
    AlphaBusX() {
        this.variant = "X Edition";
    }
    // some more properties exclusive to this variant
}

class AlphaBusY : AlphaBus {
    AlphaBusY() {
        this.variant = "Y Edition";
    }
    // some more properties exclusive to this variant
}

class BetaTruckF1 : BetaTruck {
    BetaTruckF1() {
        this.variant = "Model F1";
    }
    // some more properties exclusive to this variant
}

class BetaTruckF2 : BetaTruck {
    BetaTruckF2() {
        this.variant = "Model F2";
    }
    // some more properties exclusive to this variant
}

然后最后:

var data = new Set<Model> {
    new AlphaBusX(),
    new AlphaBusY(),
    new BetaTruckF1(),
    new BetaTruckF2()
}

API #1:

var result = data.First(x => x.manufacturer.name = <manufactuer> 
                          && x.name = <model>
                          && x.variant = <variant>);

API #2:

var result1 = API#1(<manufacturer1>, <model1>, <variant1>);
var result2 = API#1(<manufacturer2>, <model2>, <variant2>);

API #3:

var result = data.Where(x => x.price >= <price>);