2011-06-01 44 views
3

我有多个不允许修改对方字段的类,而是必须通过将请求对象添加到Main类的队列来请求修改。在每个循环结束时,Main类将执行请求的修改。“变量赋值请求”对象设计

public class Main { 

    public static ClassA a = new ClassA(); 
    public static ClassB b = new ClassB(); 

    private static List<Request> requestQueue = new ArrayList<Request>(); // List holds all requests 

    public static void main(String args[]) { 
     while (true) { 
      a.tick(); 
      b.tick(); 
      for (Request r : requestQueue) { 
       r.field = r.value; // All requests are fulfilled 
      } 
      requestQueue.clear(); 
     } 
    } 

    public static <ValueType> void addModificationRequest(ValueType field, 
      ValueType value) { 
     requestQueue.add(new Request<ValueType>(field, value)); 
    } 
} 

// Request Object 
public class Request<ValueType> { 

    ValueType field; 
    ValueType value; 

    public Request(ValueType field, ValueType value) { 
     this.field = field; 
     this.value = value; 
    } 
} 

// Classes 
public class ClassA { 
    String name = "A"; 

    public void tick() { 
     Main.addModificationRequest(Main.b.name, "NewNameB"); // Try to change b name 
    } 
} 

public class ClassB { 
    String name = "B"; 

    public void tick() { 
     // Nothing 
    } 
} 

我知道代码是乱,但它的最简单的方法我已经想到了这么远。有没有人知道任何可以以更简单,更稳健的方式实现这一点的优秀设计模式? (注:我必须这样说,没有允许类直接修改对方的可能性。)

编辑:类将有一个以上的可修改的变量。我还需要确定是否有任何请求尝试修改同一个变量,因此我可以忽略其中的一个或两个。

编辑:感谢所有的帮助,我真的很感激它!我决定让这些课程跟踪他们自己的修改。我也将使用我一直使用的相同的请求对象,因为它很简单直接。再次谢谢你。

+0

有趣的问题。你为什么不能直接修改对象? – Buhb 2011-06-01 04:09:49

+0

请注意,** a **&** b **的滴答功能是如何连续调用的。如果** a **在打勾前允许修改** b **,则会改变** b **的打勾结果。如果有很多班级,我打算有数千人,这会导致巨大的变化。解决方法是在每个循环结束前不要修改,加上主类应该能够对请求进行排序,并且作为示例,引发“ConcurrentModificationException”。 – scientiaesthete 2011-06-01 04:22:10

回答

1

也许这并不适用于您的用例,但您可以让对象自己跟踪未完成的修改,然后从您的主类中调用,修改实际字段本身。

如果你只需要设置操作,跟踪修改就像保持班级中每个字段的额外字段一样简单。如果您需要一系列算术运算,您可以使用一系列修改。

public class DelayedModificationField<T> { 
    private T field; 
    private T newValue; 
    public T get() { return field;} 
    public delayedSet(T value) { 
     if (newValue != null) throw new ConcurrentModificationException("error"); 
     newValue = value; 
    } 
    public void performSet() { 
     field = newValue; 
     newValue = null; 
    } 

public ExampleClass { 
    private DelayedModificationField<String> myField = new DelayedModificationField<String>(); 
    public void setMyField(String s) { myField.delayedSet(s);} 
    public String getMyField() { return myField.get();} 
    public void performSet() { myField.performSet();} 
} 

主类必须跟踪支持延迟修改的所有对象,并呼吁performSet他们每个刻度。让他们实现一个接口并将它们保存在一个列表中。

+0

请参阅编辑。不幸的是,我需要一个控制类来防止类彼此冲突。 – scientiaesthete 2011-06-01 04:34:18

+0

该类本身是否可以对同一字段进行两次或多次修改,以决定忽略哪些修改?在这种情况下,跟踪列表中可能的修改的解决方案可能仍然有效。 – Buhb 2011-06-01 04:44:00

+0

我实际上是在使用它一段时间,但后来认为这些类没有足够的信息来决定,并给每个人提供信息并编写他们自己的决策代码会很痛苦。一个统治阶级更容易,更简单。但是,我还没有排除。你有什么理由为什么你的想法可能会更好,听到一些可能会让我的选择更容易。 – scientiaesthete 2011-06-01 04:56:00

0

这不起作用。我不知道您的任务的确切参数,但我会用单一方法void setField(ValueType newValue)来定义接口Modifiable。然后我会声明ClassAClassB执行Modifiable。最后,将您的Request类重新定义为ModifiableValueType而不是两个ValueType字段。然后,您的队列循环将是:

for (Request r : requestQueue) { 
    r.modifiable.setField(r.value); 
} 

编辑:

下面是这种方法的修改是扩展到多个领域:

interface Modifiable { 
    void setName(String newName); 
    void setAge(Integer newAge); // I wish 
} 

abstract class Request<ValueType> { 
    Modifiable object; 
    ValueType value; 

    public Request(Modifiable object, ValueType value) { 
     this.object = object; 
     this.value = value; 
    } 

    abstract void execute(); 
} 

class RequestNameChange extends Request<String> { 
    public RequestNameChange(Modifiable object, String name) { 
     super(object, name); 
    } 

    void execute() { 
     object.setName(name); 
    } 
} 

// etc. 

另一种方法是使用反射,但我怀疑这不是你的任务。

+0

我应该提到这些类可能有多个变量应该可以修改,所以这是行不通的。我可以添加一个字符串来命名变量,但只是更多的工作将所有名称添加到函数中。 – scientiaesthete 2011-06-01 04:14:22

+0

我在回答中添加了几条建议。 – 2011-06-01 04:31:32

+0

这将适用于我给出的示例,但是当我有数百个对象时,为每个变量更改创建一个请求对象将是一件痛苦的事情。如果你看看我的例子,我能够传递任何变量和适当的值来请求构造函数。 (注意:这不是一项家庭作业,我的意思是把“赋值”赋值给变量) – scientiaesthete 2011-06-01 04:44:27

0

尝试“好莱坞”模式 - 它采用了“不要打电话给我们,我们会打电话给你”的风格,也就是使用“回调”代码。

像这样:用匿名类

public static class Main { 

    public static ClassA a = new ClassA(); 
    public static ClassB b = new ClassB(); 

    private static List<Runnable> requestQueue = new ArrayList<Runnable>(); // List holds all requests 

    public static void main(String args[]) { 
     while (true) { 
      a.tick(); 
      b.tick(); 
      for (Runnable r : requestQueue) { 
       r.run(); // All requests are fulfilled 
      } 
      requestQueue.clear(); 
     } 
    } 

    public static void addModificationRequest(Runnable runnable) { 
     requestQueue.add(runnable); 
    } 
} 

呼叫addModificationRequest:

Main.addModificationRequest(new Runnable() { 
    public void run() { 
     Main.b.name = "NewNameB"; 
    } 
}); 

注意,该代码将抛出一个ConcurrentModificationException如果addModificationRequest时调用了foreach循环正在执行。一个便宜的修复方法是将关键字​​添加到addModificationRequest中,并使用类对象作为锁在同步块中执行循环+清除。

编辑:

要通过在外地,所以你可以检查它,试试这个:

interface FieldUpdater extends Runnable 
{ 
    Field getField(); 
} 

static class Main 
{ 

    public static ClassA a = new ClassA(); 
    public static ClassB b = new ClassB(); 

    private static List<FieldUpdater> requestQueue = new ArrayList<FieldUpdater>(); // List holds all requests 

    private static Set<Field> seenBefore = new HashSet<Field>(); 

    public void main(String args[]) 
    { 
     while (true) 
     { 
      a.tick(); 
      b.tick(); 
      for (FieldUpdater r : requestQueue) 
      { 
       if (seenBefore.contains(r.getField())) 
       { 
        continue; // Or do something else specific 
       } 
       seenBefore.add(r.getField()); 
       r.run(); 
      } 
      requestQueue.clear(); 
     } 
    } 

    public synchronized static void addModificationRequest(FieldUpdater fieldUpdater) 
    { 
     requestQueue.add(fieldUpdater); 
    } 
} 

然后打电话给你的方法是这样的:

Main.addModificationRequest(new FieldUpdater() 
    { 
     public void run() 
     { 
      Main.b.name = "NewNameB"; 
     } 

     public Field getField() 
     { 
      return Main.class.getField("name"); 
     } 

    }); 

注意,对于清晰度我故意省略处理可以从getField()抛出的异常。此外,这不是很好的代码设计,但它确实解决了上述代码的固定问题。

+0

这将是完美的,但我需要能够看到两个请求是否重叠,这意味着他们尝试更改相同的变量。我不能允许这样做,或者必须提供一个优先于另一个的请求。 – scientiaesthete 2011-06-01 04:26:11

+0

请参阅编辑一些将执行此操作的代码 – Bohemian 2011-06-01 05:44:14