实例被认为是java中的一种不好的做法。但我不知道在这种情况下如何避免它。
假设我有一个抽象类ArtObject
,并且有一些子类,例如Painting
,Engraving
等。我有一个类Museum
,其中包含{{1} } ArrayList
s。
例如,我想打印有关ArtObject
中所有Painting
个对象的信息。
我应该这样做吗?:
Museum
还是存在更好的方法?
答案 0 :(得分:3)
您可以将方法isPainting()
添加到ArtObject
默认为return false;
,并覆盖到绘画中的return true;
。
然后你可以写
public void printAllPaintingsInfo() {
for (ArtObject ao : artObjects)
if (ao.isPainting())
System.out.println(ao);
}
或者在Java 8中,您可以编写
artObjects.filter(ArtObject::isPainting).forEach(System.out::println);
答案 1 :(得分:2)
与许多通常不赞成的语言特征一样,通常会通过实施某种“伪造”它们的机制来欺骗使用它们。但这并没有让事情变得更好。如果意味着来测试对象的类型,那么instanceof
就是正确的选择。如果你想避免它,你需要找到一种不要求你找出对象的(动态)类型的方法。
避免运行时类型检查的一种流行模式是使用 visitor 模式,该模式有效地具有重载分辨率,动态调度机制执行脏工作。如果您的真实问题与您发布的博物馆示例一样简单,那么这显然是过度的。无论如何,这是如何做到的。
为了这个例子,假设我们有Painting
和Drawings
,两者都来自ArtObject
。我们定义了一个抽象的访问者类型,它具有每个ArtObject
的方法。或者,您只能专注于少数人,并且可以使用后备“访问任何”方法。
public abstract class ArtVisitor {
public void visit(final Painting painting) {
}
public void visit(final Drawing drawing) {
}
// public void visit(final ArtObject anything) {
// }
}
我使用了抽象类而不是接口,所以我可以使用visit
方法的空默认实现。
在ArtObject
接口(或抽象类,就此而言)中,我们需要添加一个抽象的takeVisitor
方法。
public abstract class ArtObject {
private final String name;
protected ArtObject(final String name) {
this.name = name;
}
public abstract void takeVisitor(ArtVisitor visitor);
@Override
public final String toString() {
return String.format("%s (%s)",
this.name,
this.getClass().getCanonicalName());
}
}
不幸的是,我们无法实现 ArtObject.takeVisitor
,因为this
指针会有静态类型ArtVisitor
,这需要我们进行类型内省,这就是什么我们想避免。相反,我们必须 - 这是关于访客模式的最丑陋的事情 - 在我们的每个班级中覆盖它。
(如果我们添加了后备ArtVisitor.visit(ArtObject)
方法,我们可以实现一个通用的takeVisitor
方法,但我们仍然必须在派生类中重写它以根据类型进行重载解析this
指针工作。在大多数情况下,这会增加更多的混淆(和潜在的错误)而不是好处。)
public final class Painting extends ArtObject {
public Painting(final String name) {
super(name);
}
@Override
public void takeVisitor(final ArtVisitor visitor) {
visitor.visit(this);
}
public void aMethodOnlyPaintingsHave() {
// whatever...
}
}
public final class Drawing extends ArtObject {
public Drawing(final String name) {
super(name);
}
@Override
public void takeVisitor(final ArtVisitor visitor) {
visitor.visit(this);
}
}
现在我们可以直接建造博物馆了。
import java.util.List;
import java.util.ArrayList;
public final class Museum {
private final List<ArtObject> artworks;
public Museum() {
this.artworks = new ArrayList<ArtObject>();
artworks.add(new Painting("Mona Lisa"));
artworks.add(new Drawing("Madame Palmyre with Her Dog"));
artworks.add(new Painting("The Night Watch"));
}
public void printAllWorks() {
for (final ArtObject work : this.artworks) {
System.out.println(work);
}
}
public void printAllPaintings() {
final ArtVisitor paintingsPrinter = new ArtVisitor() {
@Override
public void visit(final Painting painting) {
System.out.println(painting);
// Note: We don't need any unsafe downcast here!
painting.aMethodOnlyPaintingsHave();
}
};
for (final ArtObject work : this.artworks) {
work.takeVisitor(paintingsPrinter);
}
}
public static void main(final String[] args) {
final Museum myMuseum = new Museum();
System.out.println("All ArtObjects:\n");
myMuseum.printAllWorks();
System.out.println("\n\nAll Paintings:\n");
myMuseum.printAllPaintings();
}
}
上述程序的输出是:
All ArtObjects:
Mona Lisa (Painting)
Madame Palmyre with Her Dog (Drawing)
The Night Watch (Painting)
All Paintings:
Mona Lisa (Painting)
The Night Watch (Painting)
答案 2 :(得分:1)
一种可能的替代方法是在所有ArtObjects
// Pseudocode
class ArtObject {
Enum artType = { Painting, Engraving }
}
class Painting implements ArtObject {
Painting() { artType = Painting }
}
public void printAllPaintingsInfo() {
for(int i = 0; i < artObjects.size(); i++) {
if(artObjects.artType == Painting) {
System.out.println(artObjects.get(i));
}
}
}
除了Collection
集合之外,另一个可能是为所有Painting
个对象保留单独的artObjects
。
答案 3 :(得分:1)
我不认为使用实例有任何问题。但是,如果您仍然不想使用它,则可以创建一个指示ArtObject类型的方法(例如Type.PAINTING,Type.ENGRAVING)。
答案 4 :(得分:1)
我通常在基类中定义谓词,或者添加一个显式的子类名称 实例变量。
class ArtObject {
boolean isPainting(){return(false):} boolean isEngraving(){return(false); } }
然后在子类中,将这些方法定义为true 可替代地
typedef enum typeOfArt { generic, painting, engraving };
class ArtObject { typeOfArt type = TypeOfArt.generic; }
取决于模型的复杂性,您可能会避免创建子类 完全如果由开关(类型)引起的混乱不是太大。
答案 5 :(得分:1)
如果我们这样做会怎么样?
public abstract class ArtObject{
//Classes extending this class will need to override and implement this method
abstract public Type getType();
}
public class Painting extends ArtObject {
/*
Other code probably
*/
@Override
public Type getType(){
return Type.Painting;
}
}
enum Type {
Painting, Museum;
}
public void printAllPaintingsInfo() {
for (int i = 0; i < artObjects.size(); i++) {
if (artObjects.get(i).getType() == Type.Painting) {
System.out.println(artObjects.get(i));
}
}
}