我在java中几乎没有与oop相关的问题。
我知道是关系意味着继承。
我知道可以做关系意味着我需要实现接口。
这里出现问题:
现在,如果我有更多变量,例如:
在Square广场 - > int side; 在类Rectangle中 - > int width,int height ....等等。
如果我按照建议的良好做法,即创建一个接口实例
Shape s = new Square();
然后我无法访问实例变量 side 。
所以,我不得不依赖这样的事情:
Square s = new Square();
s.side = 3;
s.draw();
在继承的情况下出现同样的问题。 例如:
public class Animal{
}
public class Reptile extends Animal{
}
public class Dog extends Animal{
}
现在,如果我尝试这样的事情:
狗有自己的变量,如牙齿,腿和眼睛。
Animal a = new Dog();
a.teeth = 30; // will not work
OR
根据这样的做法是否正确:
Dog d = new Dog();
d.teeth = 30;
那么,我错过了一些设计细节?请解释一下。
我尽力做出正确的设计。只是偶然发现了这些事情。
答案 0 :(得分:1)
您的设计是正确的(到目前为止)。具体回答你的问题:
那么,我的设计是不正确的? - 不 - 就目前而言是正确的。
实现接口的类是否应该只具有接口所具有的方法而不能用于调用目的? 不 - 它可以并且可能还有其他方法。否则,实现接口的所有类都是相同的。
你只会这样做:
Shape s = new Square();
如果你想在s上执行可以应用于任何形状的操作(例如绘制)。
如果您需要执行Square
具体操作,则需要使用Square
变量。没有什么可说的,你必须使用超级班。
我建议你继续使用你的设计并找到一些真实的单词示例,然后它会变得更清晰。
更新:
请注意,new
操作可能会在您的工厂内使用。类似地,在工厂方法中设置(或可能)设置诸如边数之类的属性。工厂总是需要知道他们正在处理的确切类型。
在工厂外,事情可能更为普遍。
因此,可能有助于您的设计(但不是很难和快速)的规则是支持使用工厂中的特定类以及它之外的Shape
类。
答案 1 :(得分:1)
属性(属于对象的变量,如Square示例中的“side”)在Java中通常是私有的。
问问自己,您想要对所有形状共有的属性做什么。如果没有任何东西,那么它们也不属于Shape。例如,宽度和高度在Rectangle中有意义,但在Circle中没有意义,所以它们不应该是Shape。
说你想要计算面积。这对所有形状都很常见,但每种形状都以不同的方式完成。
在这种情况下,你要在接口上添加一个方法(让我们称之为calculateArea()
)并实现它将不同的实现放在各个子类中,因此Square会使用side * side
来计算它,而Rectangle将使用width * height
而Circle将使用Math.Pi * radius * radius
。
所以在这种情况下,属性应该是私有的并且属于子类,因为它们在每个属性中都是不同的,而所有形状共有的东西(比如计算区域的能力)都在接口上。 / p>
我希望这会让你更清楚。
答案 2 :(得分:1)
我没有发现与您的设计相关的继承和多态问题。 (除非我会说正方形,圆形,矩形是形状所以可能使用继承代替)。可能有一些关于Java语法的东西,但是你可能会遗漏。
考虑任何声明:
A test = new B()
第一个关键字A
告诉计算机变量的类型。在这种情况下,您可以保证计算机无论您的变量引用什么都是A
。第二个关键字test
只是变量的名称。第三部分= new B()
告诉计算机您正在指定test
来引用B
类型的新对象。只要B
是A
,您就可以了。
现在让我们考虑第一个例子,Square
和Shape
。如果你说
Shape s = new Square()
您告诉计算机s
是对Shape
的引用 - 即"无论s
指的是什么,总是Shape
。当您设置= new Square()
时,您说它希望它引用Square
。这很好,因为Square
是 Shape
。但是,您无法说出s.side = 3
,因为s
您只保证s
引用Shape
的计算机而不是所有Shape
只有一方长度。
为了矛盾,假设您应该能够访问s.side。那你将如何调和这两个代码块呢?
Square s = new Square();
s.side = 3;
System.out.println( s.side );
Shape s = new Square();
s.side = 3;
s = new Circle();
System.out.println( s.side );
显然,Circle
没有一个会绕过"绕过"我们的逻辑。
另一方面,如果你说
Square s = new Square();
您保证所有s
所指的计算机都具有side
属性,因此您可以访问s.side
。
添加:您问题的具体答案"所以,我错过了一些设计细节?"
我不认为你错过了设计细节。我认为你只是没有得到代码的含义。例如,有些情况下你想说
Animal a = new Dog();
并且有些情况下你想说
Dog a = new Dog();
在确定您想要使用的类型标识符(动物或狗)时,您会问自己一个问题,"我是在操作动物还是狗?"如果您正在动物身上操作,请说
Animal a = new Animal();
然后,您可以执行更复杂的操作。例如,如果您有动物列表且所有动物都有方法live()
,那么您可以浏览列表并制作所有动物live()
,而不必担心它是狗还是猫?骆驼。代码:
ArrayList< Animal > listOfAnimals = new ArrayList< Animal >();
//populate the list
for ( Animal a : listOfAnimals ) {
a.live();
}
如果您只对狗进行操作(例如,您需要全部咬狗),那么您必须使用Dog
标识符。代码:
ArrayList< Dog > listOfDogs = new ArrayList< Dog >();
//populate the list
for ( Dog d : listOfDogs ) {
d.bite();
}
如果相反,我们会宣布ArrayList< Animal > listOfDogs = new ArrayList< Animal >();
并使每个元素bite()
成为什么问题?