实例化另一个对象

3
在Java中,我可能有一个对象a,其中包含像getColor这样的许多属性。然后,我可能会创建第二个对象b(可能是相同类的对象),其行为类似于对象a,因为它做与a相同的事情,除非其中一个值被特定更改(也未更改)。
a = new Foo()
b = new FooWhichRefersToAnotherFoo(a)

a.getColor() # returns blue
b.getColor() # returns blue
a.setColor(red)
b.getColor() # returns red
b.setColor(pink)
b.getColor() # returns pink
a.getColor() # returns red (unchanged)
b.deleteChangeToColor()
b.getColor() # returns red as it is now pointing back to a

我认为这应该是一种树形层次结构,就像我有一个指向a的指针c指向b,对象将沿着链路向上工作,直到找到第一个指定值或未设置父对象的默认值。
是否存在类似于这样的模式,适用于Java? 我期望第一个类是一个类,第二个类是继承类,它跟踪实例化的类,并且如果它自己的属性未设置,则查询父对象。
我想我可以自己完成这个过程,每个我创建的类,我可以创建一个单独的类。
class Foo {
  Color color = new Color("red");
  Color getColor() { color }
}

class FooInstance extends Foo {
  Foo parent = null;

  FooInstance(Foo parent) {
    this.parent = parent;
  }

  Color getColor() {
    if (color == null) return parent.getColor();
    else return color;
  }
}

但我希望确认一下是否有更简单的机制,例如使用JavaBeans之类的东西。继承的一个问题是它会暴露父类的所有方法,而我可能想要指定哪些方法实际上在子类中可用,所以也许需要完全分离一个独立的类?


谁说的?http://dictionary.reference.com/browse/instance - voodoogiant
5
不论词典怎么说,“instantiate”通常是编程中用来表示“创建实例”的动词形式。 - Michael McGowan
但是我这里所指的实例并不是实例化对象时指的那种实例。在这里,“实例”可以与父对象完全无关,例如在三维场景中的“实例”对象。也许“实例”是一个不太合适的选择,但“实例化”会有其他意义。我稍微重新措辞一下我的问题。谢谢。 - voodoogiant
如果你不是指实例化,那么我认为“instance”这个词选择得不太好,因为它的意思不太清楚。 - Michael McGowan
对不起,我现在意识到我的措辞选择很不恰当,已经更新了问题。我认为混淆的原因在于编程中使用的单词与用户界面呈现不同。在许多程序中,您会看到“资产”(尽量不要混淆术语),您可以将它们“实例化”,实例会像我在帖子中描述的那样运作。我觉得,尽管有歧义,包含这个术语会增加理解我的目的的可能性。 - voodoogiant
4个回答

1
我已经读了你的帖子几遍,有一些事情仍然让我感到困惑。例如,在你的a/b/c示例中,你谈到行为除非值不同否则相同。我认为你需要更清楚地区分行为和状态的概念。行为是一个类将要做的事情,状态是它属性的值。一个类的行为通常取决于状态,即:
Color getColor() {
    if (color == null) return parent.getColor();
    else return color;
}

但它们是两个不同的东西。从你的例子中,我认为你不需要两个或更多不同的类。你可以将Foo和FooInstance重新编码为一个单一的类,如下所示:

class Foo {
    Foo parent = null;
    Color color;

    Foo(Foo parent) {
        this.parent = parent;
    }

    Color getColor() {
        //If there is no parent we have to return color regardless.
        if (parent == null) {
            return color;
        }

        // If there is a parent we can choose which to return.
        return color == null ? parent.getColor() : color;
    }

    void setColor(Color color) {
        this.color = color;
    }
}

除非您需要不同的FooInstance行为,否则您可以使用单个类来完成所需操作。

我不知道是否有任何第三方API提供此类数据结构。但是它们可能存在。


在过去的几天里,我在这里和互联网上看了各种选项,这似乎是我需要的最强大的选择。我想我原本希望有一个魔法子弹,但我认为它可能会抽象化我需要的很多控制。谢谢。 - voodoogiant

1

java.util.Properties有一个鲜为人知的特性,可以让你创建这种层次结构而无需编写特殊代码:

package stackoverflow;
import java.util.Properties;

public class Main {
    static Properties propsBase;
    static Properties propsOverlay;
    static Properties propsOverlayOverlay;

    public static void main(String[] args) {
        propsBase = new Properties();
        propsOverlay = new Properties(propsBase);
        propsOverlayOverlay = new Properties(propsOverlay);

        propsBase.setProperty("key1", "value1");
        propsBase.setProperty("key2", "value2");
        debugAllProps();

        propsOverlay.setProperty("key1", "overlayValue1");
        debugAllProps();

        propsOverlayOverlay.setProperty("key1", "overlayOverlayValue1");
        debugAllProps();

        propsOverlayOverlay.remove("key1");
        debugAllProps();

        propsOverlay.remove("key1");
        debugAllProps();
    }

    private static void debugAllProps() {
        printProps("propsBase", propsBase);
        printProps("propsOverlay", propsOverlay);
        printProps("propsOverlayOverlay", propsOverlayOverlay);
        System.out.println("------------------------------------------------");
    }

    private static void printProps(String desc, Properties props) {
        System.out.printf("%-25s", desc + " sees:");
        for (String key : props.stringPropertyNames()) {
            System.out.printf(" %s=%s", key, props.getProperty(key));
        }
        System.out.println();
    }
}


输出:

propsBase sees:           key2=value2 key1=value1
propsOverlay sees:        key2=value2 key1=value1
propsOverlayOverlay sees: key2=value2 key1=value1
-------------------------------------------------
propsBase sees:           key2=value2 key1=value1
propsOverlay sees:        key2=value2 key1=overlayValue1
propsOverlayOverlay sees: key2=value2 key1=overlayValue1
-------------------------------------------------
propsBase sees:           key2=value2 key1=value1
propsOverlay sees:        key2=value2 key1=overlayValue1
propsOverlayOverlay sees: key2=value2 key1=overlayOverlayValue1
-------------------------------------------------
propsBase sees:           key2=value2 key1=value1
propsOverlay sees:        key2=value2 key1=overlayValue1
propsOverlayOverlay sees: key2=value2 key1=overlayValue1
-------------------------------------------------
propsBase sees:           key2=value2 key1=value1
propsOverlay sees:        key2=value2 key1=value1
propsOverlayOverlay sees: key2=value2 key1=value1

这种方法有一些重要的限制:

  • 因为这是java.util.Properties,所以只能存储和检索String值。

  • 你必须小心地只使用由java.util.Properties定义的访问器方法,因为其超类Hashtable定义的方法不知道层次结构并忽略它。

如果我要做任何严肃的事情,我可能不会使用java.util.Properties,而是设计自己的集合,以Properties的一般方法为灵感。你可以通过查看与JDK一起安装的src.zip来阅读java.util.Properties的源代码。

我的集合理解是

  • 允许对象键和对象值
  • 具有允许检查和修改层次结构的方法
  • 可能不会对任何现有集合进行子类化 - 主要是为了避免使每个超类方法都“层次感知”。可能更喜欢decorate一个HashMap
  • 如果有意义,可能会实现一些java.util集合接口
  • 可能支持泛型

0

看起来你正在尝试实现类似于CSS的行为,特别是“级联”部分。这不是一个特别常见的模式,但我以前曾经实现过这种东西。在我看来,我不会将类型拆分成FooFooInstance - 我会创建一个单一的类,以一般方式处理此模式,可能通过使用HashMap来处理属性值。通过这种方式,您可以自动处理空值情况并向父级冒泡。像这样:

public class Foo {
    private Foo parent;
    private HashMap<string, Object> propertyValues = new HashMap<string, Object)>();

    public Foo() {
    }

    public Foo(Foo parent) {
        this.parent = parent;
    }

    protected Object getProperty(string propertyName) {
        if (properties.containsKey(propertyName))
            return properties.get(propertyName);
        else if (parent != null)
            return parent.getProperty(propertyName);
        else
            return null;
    }

    protected void setProperty(string propertyName, value) {
        properties.put(propertyName, value);
    }

    public Color getColor() {
        return (Color)getProperty("color");
    }

    public void setColor(Color color) {
        setProperty("color", color);
    }
}

0

我会使用装饰器模式来实现这个。两个类不一定需要相同,但需要实现一个公共接口,并在构造函数中将另一个类传入,然后让getter方法将处理权移交给被包装的类,直到其中一个在包装类中被覆盖。在这个例子中,我仅使用了一个类来简化,但可以灵活处理任何Colorful。从测试开始:

import junit.framework.TestCase;

import java.awt.*;

public class CascadingTest extends TestCase {

    public void testCascade() throws Exception {
        Colorful a = new Foo();
        a.setColor(Color.RED);
        assertEquals(Color.RED, a.getColor());

        Colorful b = new Foo(a);
        assertEquals(Color.RED, b.getColor());

        b.setColor(Color.PINK);
        assertEquals(Color.PINK, b.getColor());

        b.setColor(null);
        assertEquals(Color.RED, b.getColor());
    }
}

import java.awt.*;

public interface Colorful {
    Color getColor();
    void setColor(Color color);
}

import java.awt.*;

public class Foo implements Colorful {

    private Color color;
    private Colorful parent;

    public Foo() {}

    public Foo(Colorful parent) {
        this.parent = parent;
    }

    public Color getColor() {
        if (parent != null && this.color == null) {
            return parent.getColor();
        } else {
            return color;
        }
    }

    public void setColor(Color color) {
        this.color = color;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接