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>);
这是在采访中被问到的。
有不同的巴士制造商。每辆巴士都有不同的型号,每个型号只有 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>);