有关解决this问题的建议吗?
答案 0 :(得分:13)
嗯,这是我提出的一个好方法 - 利用OOP覆盖,子类和超类:
namespace Animals{
// base class Animal
class Animal{
public void eat(Food f){
}
}
class Carnivore extends Animal{
public void eat(Meat f){
}
}
class Herbivore extends Animal{
public void eat(Plant f){
}
}
class Omnivore extends Animal{
public void eat(Food f){
}
}
}
namespace Food{
// base class Food
class Food{
}
class Meat extends Food{
}
class Plant extends Food{
}
}
我从超类Animal中创建了Herbivore,Carnivore和Omnivore的子类,并使用它实际可以吃的食物类型覆盖eat
方法。
所以:
Plant grass = new Plant();
Herbivore deer = new Herbivore();
deer.eat(grass); // ok
Plant grass2 = new Plant();
Carnivore tiger = new Carnivore();
tiger.eat(grass2); // not ok.
Meat deer2 = new Meat();
tiger.eat(deer2); // ok
嗯,最后一个问题是,当您指定deer
是Herbivore
时,您无法使Meat
成为tiger
。然而,在一天结束时,这应该足以解决面试问题,同时不让面试官入睡。
答案 1 :(得分:8)
Liskov Substitution Principle有一张精彩的海报,上面写着:“如果它看起来像鸭子,像鸭子一样嘎嘎叫,但需要电池,你可能会得到错误的抽象。”这是快速的答案 - 一些对象可以是动物和食物,所以除非你愿意走多重继承的路线,否则分类模式都是错误的。
一旦你清除了障碍,剩下的就是开放式的,你可以引入其他设计原则。例如,您可以添加允许使用对象的IEdible接口。您可能会面向方面,并为食肉动物和食草动物添加装饰器,这将允许仅使用正确类别的对象。
重点是能够思考你的脚,看到并解释问题的各个方面,并进行良好的沟通。也许不会陷入“一个正确答案”的限制。
答案 2 :(得分:6)
我告诉他要抓一点。这是一个可怕的抽象。更不用说我们没有任何背景。抽象不是凭空而出,也不是出于对“正确”的“想法”。告诉我你先试着解决什么问题,这样我们就可以评估这种抽象。
如果没有提供上下文,那么我将假设/化妆我自己:你希望某些类型的对象能够吃掉其他类型的对象。没什么,没什么。
创建一个Eatable
界面(或者你可以称之为Food
,如果你愿意的话),因为我们没有上下文,所以我认为它是一个玩具控制台程序,只是打印:
<X> ate <Y>
所以我们所需要的只是getFoodName()
方法。
对于错误检查,您可以创建一堆isXFoodType
方法,例如isGrassFoodType()
,isMeatFoodType()
等。Cow
的{{1}}实现将检查Eat(Eatable e)
,当失败时,打印:
isGrassFoodType()
答案 3 :(得分:4)
试图解决数据模型中的这个“问题”听起来像是后期绑定的反面:为什么需要编译器强制执行此操作?我根本不担心改变模型。如果你传递了一些你不能吃的东西,你会抛出一个例外 - 就像在现实生活中一样,非常多!
答案 4 :(得分:3)
食物应该是一个界面,因此植物和动物也可以是食物。
抽象动物类应该有以食物为参数的吃法。
动物的子类:食肉动物,草食动物和杂食动物应该有自己的吃的版本。例如Carnivore:
private void eat(Food food)
{
if(food instanceof Animal)
{
happilyEat();
}
else
{
sniff&TurnAway();
}
}
问题解决了。
但是对于更好的设计,Carnivore,Herbivore和Omnivore也应该是接口,因为它们不是标记动物的正确方法。
答案 5 :(得分:2)
使用Generics在C#btw中很容易:
public class Food
{
}
public abstract class Animal<T> : Meat where T:Food
{
public abstract void Eat(T food);
}
public class Herbivore : Animal<Plant>
{
public override void Eat(Plant food)
{
Console.WriteLine("Herbivore eats plants.");
}
}
public class Omnivore : Animal<Food>
{
public override void Eat(Food food)
{
Console.WriteLine("Omnivore eats food.");
}
}
public class Carnivore : Animal<Meat>
{
public override void Eat(Meat food)
{
Console.WriteLine("Carnivore eats meat.");
}
}
public class Plant : Food
{
}
public class Meat : Food
{
}
public class Cow : Herbivore
{
}
public class Tiger : Carnivore
{
}
public class Human : Omnivore
{
}
用法:
var human = new Human();
var tiger = new Tiger();
var cow = new Cow();
var plant = new Plant();
human.Eat(cow);
tiger.Eat(human);
cow.Eat(tiger); // this doesn't compile
tiger.Eat(plant); // neither does this
答案 6 :(得分:1)
以下是对面试问题的一些看法:
我同意Cylon Cat:如果没有多重继承,这种抽象就行不通(即使它是类似Java的接口。)
我会创建两种形式的继承:
动物:
食物:
两种动物的“吃”方法(我忽略了杂食动物,食虫动物和许多其他种类)将专门针对不同种类的食物。如果我们使用像Java这样的语言,那么Food就是一个界面。
答案 7 :(得分:1)
任何动物都是食物,任何蔬菜都是食物。事实上,老虎可以被一头牛吃掉。 (通过将感染的绵羊神经组织喂给未感染的绵羊来传播朊病毒病瘙痒病。)
你可以拥有物种的等级,ala Linnaeus,动物和蔬菜。每个物种都是一个单身人士,它有List<Species>
记录其典型的饮食。彻底抛弃食物层次结构,它只会混淆事物。
而且,如果您唯一的问题是记录每个物种的饮食,那么多种物种类是不必要的。只需要一个Species类,其中物种名称作为一个实例变量,List<Species>
作为另一个实例变量。
答案 8 :(得分:0)
没有一个最好的解决方案。您可能希望将其作为社区维基,因为这通常是主观问题的公认实践。
我会考虑实际上会为层次结构制作最佳父类的内容,并尝试弄清楚在这种情况下“最佳”的含义。
所有东西都有东西,可以有一个Name属性。但某种程度上的一切都是食物;如果它可以吃东西,有东西可以吃。我可能会将Food作为父类,并为它提供一个返回布尔值的方法,以检查参数对象是否可以吃掉当前对象。
所以,
class Food {
boolean canBeEatenBy(Food hungryObject)
String name
}
这似乎是最简单的类层次结构,适合我在第一次传递时可能需要的所有内容?
那就是说,大多数面试问题的重要部分是你对受访者的感受,而不是他们给出的确切答案。
答案 9 :(得分:0)
Double dispatch,也许?
答案 10 :(得分:0)
如果您希望系统变得非常大,我建议将植物/肉类和食草动物/食肉动物/杂食动物分类。
确保系统具有所有植物/动物的标准接口,称为getFoodName()和getFoodType(),您可以通过为称为物种的植物/动物创建父类来强制执行此操作。
我会看到子类化植物/肉类和食肉动物/食草动物的问题是猫鼬是食肉动物但它可能不能吃犀牛(可能有更好的例子),所以除了“I”之外还需要一些限制吃肉,你吃植物“。
如果它不会变得非常大并且你想对它神经质,你可以为动物的每个子类存储允许食物的静态枚举。所以老虎可以存放鹿,羚羊等。