1

我试图模仿以下抽象类,旨在仅启用一个惰性初始化,而不使用逻辑语句。为了简单起见,我忽略了线程安全所需的同步元素。

abstract class Thunk<T>
{
    private boolean initiated = false;
    private T value;

    public T get()
    {
        if(!initiated) // not using (value == null)
        {
            value = compute();
            initiated = true;
        }

        return value;
    }

    abstract protected T compute();
}

以下抽象类的实例是否可以被孩子破解以多次初始化同一个变量?

abstract class Thunk<T>
{
    private T value;
    private Computer<T> computer;

    public Thunk()
    {
        computer = new Computer<T>(this);
    }

    public T get()
    {
        value = computer.getValue();    
        return value;
    }

    abstract protected T compute();

    private class Computer<T> 
    {
        private static final String TAG = "Computer";

        private Thunk<T> thunk;
        private T value;
        private Computer<T> computer;

        public Computer(Thunk<T> thunk)
        {
            Log.d(TAG, "constructed");
            this.thunk = thunk;
            computer = this;
        }

        public T getValue() 
        {
            Log.d(TAG + ".getValue()", "");
            value = computer.computeValue();
            return value;
        }

        protected T computeValue() 
        {
            Log.d(TAG + ".computeValue()", "");
            value = thunk.compute();
            computer = new DumbComputer<T>(thunk, value);
            return value;
        }

        //this is for maximal encapsulation
        private class DumbComputer<T> extends Computer<T>
        {
            private static final String TAG = "DumbComputer";
            private T value;

            public DumbComputer(Thunk<T> thunk, T value) 
            {
                super(thunk);
                Log.d(TAG + ".contructed()", "booki");
                this.value = value; 
            }

            //overriding so that value will be calculated only once.
            @Override
            protected T computeValue() 
            {
                Log.d(TAG + ".computeValue()", "");
                return value;
            }
        }

    }
}
4

3 回答 3

1

是的,通过覆盖该get方法。

要解决这个问题,您可以将其get变成一种final方法。这将防止覆盖并为您提供类似单例的行为。

请注意,您编写的代码不是线程安全的。

您可以通过制作方法来实现线程安全synchronized(不要担心性能,直到您知道您给出了问题并且该方法是热点,因为慢速正确代码比快速错误代码更好,并且JVM非常擅长优化锁。如果你发现这个类的特定锁过热,你可以使用一些技巧来加速它......但现在不要担心

另外值得指出的是惰性初始化的资源持有者内部类模式(不适用于您的用例,因为此类需要。它仅用于单例)如果您想要最好的单例惰性初始化,则可以使用。

更新(回复评论,因为评论不支持格式化)

做这个:

abstract class Thunk<T>
{
    private boolean initiated = false;
    private T value;

    public synchronized final T get()
    {
        if(!initiated) // not using (value == null)
        {
            value = compute();
            initiated = true;
        }

        return value;
    }

    abstract protected T compute();
}

这是可能工作的最简单的代码。甚至不要梦想尝试“改进”该代码。它可以改进,但改进会根据类的使用方式而有所不同,并且改进的复杂性将隐藏你的代码试图做什么。从可以工作的最简单的事情开始,然后从那里开始。

保持简单愚蠢

不要解决你还没有的问题

于 2012-11-25T08:44:43.637 回答
1

图案

public final void f() {
    ...
    X x = ...;
    g(x);
    ...
}

abstract protected void g(X x);

在契约式编程中非常有用:

  • 强加一种行为(f 的主体),并且
  • 提供本地上下文 (x)。

一个行为通常是通过持有一个状态来实现的(比如你的initiated)。所以是的,懒惰的评估很好。尽管可以在现场级别上实现惰性评估,例如通过很少见的宝石Future<>

于 2012-11-26T10:50:46.547 回答
0

您的第二个示例无法(可能)按预期工作,因为您DumbComputer每次调用时都会创建一个新示例Thunk.get。您可以按如下方式实现您的目标(但我认为这不是一个好的设计,而且我真的看不出与更简单的解决方案相比的优势在哪里):

abstract class Thunk<T> {

    T value;
    Computer<T> computer;

    protected abstract T doCompute ();

    private interface Computer<T> {
        Computer getComputer ();
        T compute ();
    }

    public Thunk<T> () {
        // initialize computer with a calculating one
        computer = new Computer<T> () {
            Computer getComputer () {
                // return a dumb computer
                return new Computer<T> () {
                    Computer getComputer () { return this; }
                    T compute () { return value; }
                }
            }
            T compute () { value = doCompute (); return value; }
        };
    }

    public T getValue () {
        T v = computer.compute (); computer = computer.getComputer (); return v;
    }
}
于 2012-11-25T08:41:47.217 回答