Java - 这是一个成语或模式,没有状态的行为类

时间:2011-01-14 15:44:13

标签: java functional-programming design-patterns idioms

我正在尝试将更多功能编程习惯用法融入到我的java开发中。我最喜欢并避免副作用的一种模式是构建具有行为的类,但它们不一定具有任何状态。行为被锁定在方法中,但它们仅对传入的参数起作用。

下面的代码是我试图避免的代码:

public class BadObject {

    private Map<String, String> data = new HashMap<String, String>();

    public BadObject() {
        data.put("data", "data");
    }

    /**
     * Act on the data class. But this is bad because we can't
     * rely on the integrity of the object's state.     
     */
    public void execute() {
        data.get("data").toString();
    }

}

下面的代码没有什么特别之处,但是我正在对参数进行操作,并且状态包含在该类中。我们仍然可能遇到这个类的问题,但这是方法和数据状态的问题,我们可以解决例程中的问题而不是不信任整个对象。

这是某种形式的成语吗?这类似于您使用的任何模式吗?

public class SemiStatefulOOP {

    /**
     * Private class implies that I can access the members of the <code>Data</code> class
     * within the <code>SemiStatefulOOP</code> class and I can also access
     * the getData method from some other class.
     *  
     * @see Test1
     *
     */
    class Data {
        private int counter = 0;        
        public int getData() {
            return counter;
        }
        public String toString() { return Integer.toString(counter); }
    }

    /**
     * Act on the data class. 
     */
    public void execute(final Data data) {
        data.counter++;        
    }

    /**
     * Act on the data class. 
     */
    public void updateStateWithCallToService(final Data data) {
        data.counter++;       
    }

    /**
     * Similar to CLOS (Common Lisp Object System) make instance.
     */
    public Data makeInstance() {
        return new Data();
    }

} // End of Class //

上述代码的问题:

  1. 我想将Data类声明为private,但是我无法在类之外引用它:

  2. 我无法覆盖SemiStateful类并访问私有成员。

  3. 用法:

    final SemiStatefulOOP someObject = new SemiStatefulOOP();
    final SemiStatefulOOP.Data data = someObject.makeInstance(); 
    
    someObject.execute(data);
    someObject.updateStateWithCallToService(data);
    

    编辑-1:这是一个很好的评论。我的回答是:“只要你在主类之外访问Data类,你就会暴露实现细节,” - 评论。

    我的回复:Data类是一个简单的POJO,可以像其他pojos一样使用setter和getter。我在上面的课程中所做的只是尝试从行为类SemiStatefulOOP中操作Data类。我确实打算使用无状态类,但我希望与状态类和行为类明确分离。

    相关:

    无状态设计模式 http://guides.brucejmack.biz/Pattern%20Documents/Stateless%20Design%20Pattern.htm

6 个答案:

答案 0 :(得分:3)

OO编程的一个关键点是隐藏实现细节。您的方法似乎并没有这样做 - 只要您在主类之外访问Data类,您就会公开实现细节,并有效地公开数据表示。

当然不可能让所有类都无状态 - 某些东西必须保持状态,而且我不清楚为什么在数据中保存它比在主类中保存它更好。

最后,OO编程的原则是将与其相关的数据和功能保持在同一个地方,即同一个类。简而言之,虽然您的提案很有意思,但我认为它所带来的问题比它解决的问题更糟糕。

答案 1 :(得分:3)

有一种有趣的OO架构风格,旨在将数据和系统行为分开,以便它们可以独立发展:the DCI Architecture

实际上,您为域概念创建数据对象(可能只有与数据本身相关的简单行为);和行为对象,它们使用数据对象并实现系统的用例。这些行为对象被视为域对象可以扮演的角色,并且使用trait (pdf)的OO概念实现。

Scala有特征,但Java没有。您可以尝试在Java中使用Qi4J framework

答案 2 :(得分:2)

  

构建具有行为的类   但他们不一定有任何   状态

请参阅wiki

策略模式旨在提供一种定义算法族的方法,将每个算法封装为一个对象,并使它们可互换。策略模式允许算法独立于使用它们的客户端。

// The context class uses this to call the concrete strategy
interface Strategy {
    int execute(int a, int b); 
}

// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyAdd's execute()");
        return a + b;  // Do an addition with a and b
    }
}

答案 3 :(得分:1)

我不确定我完全明白你在这里问的是什么。作为BadObject有状态的替代方法,你不能简单地将方法声明为

public void execute(Map<String, String> data) {
    ...
}

或类似的?

一般来说,当我想到功能性和/或无状态习语时,压倒性的代码模式就是让方法为它们所依赖的一切获取参数(而不是从字段或静态方法或训练残骸中获取它们({ {1}}))。那和返回结果,而不是修改其他对象的状态。

此时,所有数据类都是不可变的,因为没有什么可以修改的,这使得代码更容易理解和推理。

这不是最初的GoF模式之一,但我相信Josh Bloch在 Effective Java 中有一个段落,其标题为“Prefer immutability”,这个内容非常吸引人。

答案 4 :(得分:1)

我认为将更多函数式编程合并到项目中的最佳解决方案是使用函数式编程语言,如Scala。 Scala可与Java完全互操作,并与JVM兼容。 Scala类是Java类,反之亦然。为什么不试试...... :)

Java是完整的OOP语言,我认为功能范式并不适合它。

答案 5 :(得分:0)

看起来你有实用程序类。

public class Data {    
    private static final Map<String, String> data = new HashMap<String, String>();
    static {
        data.put("data", "data");
    }
    private Data() { }

    /**
     * Act on the data class.     
     */
    public static void execute() {
        data.get("data").toString();
    }
}

您无需创建对象。

Data.execute();