【问题标题】:Varying the order of method calling for different instances of a class改变调用类的不同实例的方法的顺序
【发布时间】:2015-05-23 21:10:40
【问题描述】:

根据某些条件(而不是用不同的顺序再次编写它们)来操作顺序的最佳方法是什么?

假设有一个 Person 类,每个 Person 对象代表一个不同的人。

class Person{
    int eatingPriority = 3;
    int sleepingPriority = 2;
    int recreationPriority = 1;

    void eat() {/*eats*/}
    void sleep() {/*sleeps*/}
    void watchTv() {/*watches tv*/}

    void satisfyNeeds() {
        //HOW TO DO THIS
    }
}

如何让satisfyNeeds() 方法根据优先级调用其他三个方法?

注意:我想明确指出,优先级可以因人而异。

【问题讨论】:

  • 最简单的方法是使用 2 个方法创建一个抽象类:getPriority() 和 run()。然后为每个活动创建一个实例。最后,您可以将这些活动的实例放在一个列表中,并按优先级对其进行排序。
  • @ArnaudDenoyelle 或使用PriorityQueue
  • 有人问我这个程序的目的。我们正在努力寻找最简洁的方式来操纵程序的流程。在这个例子中,我们假设我们正在为一个有模拟人的游戏创建一个 AI。如果你又饿又累,你会先睡觉还是先吃饭?如果您的睡眠优先级较高,则您先睡觉,否则您在睡觉前吃饭。也可以即时更改优先级。我们可以衡量睡眠和食物需求,并以此为基础设定优先级。注意问题中的代码是为了演示问题的简化版本

标签: java methods call


【解决方案1】:

您可以使用 1 个类和 1 个接口来做到这一点。

public class Person {
    int eatingPriority = 3;
    int sleepingPriority = 2;
    int recreationPriority = 1;

    PriorityQueue<Action> actions;

    void eat() { }

    void sleep() { }

    void watchTv() { }

    public Person() {
        actions = new PriorityQueue<Action>(new Comparator<Action>() {
            @Override
            public int compare(Action o1, Action o2) {
                return o2.getPriority() - o1.getPriority();
            }
        });

        actions.add(new Action() {
            @Override
            public int getPriority() {
                return eatingPriority;
            }
            @Override
            public void execute() {
                eat();
            }
        });

        actions.add(new Action() {
            @Override
            public int getPriority() {
                return sleepingPriority;
            }
            @Override
            public void execute() {
                sleep();
            }
        });

        actions.add(new Action() {
            @Override
            public int getPriority() {
                return recreationPriority;
            }
            @Override
            public void execute() {
                watchTv();
            }
        });
    }

    public void satisfyNeeds() {
        for (Action action : actions) {
            action.execute();
        }
    }

    interface Action {
        public int getPriority();
        public void execute();
    }
}

【讨论】:

  • 您还可以保留您的类结构并从适当的 execute() 方法调用您的方法 eat()、sleep() 和 watchTv()。
  • 谢谢!好主意,这里使用 PriorityQueue 代替 ArrayList 是合理的。
  • 是的,对不起,我删除了我上面的评论,以便写一个更解释的评论。在这种情况下,PriorityQueue 优于 ArrayList 的优势在于排序过程是在每次添加元素时进行的,尤其是“持久化”。通过使用 ArrayList,无论何时您调用 satisfyNeeds,您都必须对列表进行排序,在某些用例中可能会非常消耗。
  • 是的,非常棒;)
  • 我什至会继承 Action 类以使用一些有意义的术语,例如 SleepAction,并保持打开/关闭原则;)
【解决方案2】:

这是另一种可能的实现:

abstract class Need {
  abstract void satisfy();
}

class Eat extends Need {
  @Override
  public void satisfy() { /* eat ...*/}
}

class Sleep extends Need {
  @Override
  public void satisfy() { /* sleep ...*/}
}

class DrinkBeer extends Need {
  @Override
  public void satisfy() { /* drink beer ...*/}
}

class Person{
  // TreeMap will sort the map in the key's natural order (a int here)
  private Map<Integer, Need> needs = new TreeMap<>();    

 Person() {
   add(new Eat(), 3);
   add(new Sleep(), 2);
   add(new DrinkBeer(), 1);
 }

 void add(Need need, int priority) {
   needs.put(Integer.valueOf(priority), need);
 }

 void satisfyNeeds() {
    for(Need need : needs.values())
      need.satisfy();
  }
} 

【讨论】:

  • needs.put(Integer.valueOf(priority), need) 可以简单地写成needs.put(priority, need)。
【解决方案3】:

此解决方案需要 Java 8:

class Person {

    void eat() {};
    void sleep() {};
    void watchTv() {};

    // Being in a List you can easily reorder the needs when you want to
    List<Runnable> needs = Arrays.asList(this::eat, this::sleep);

    // Alternatively, you can use a Map<Runnable, Integer> where the value is your
    // priority and sort it (see http://stackoverflow.com/q/109383/1296402)

    void satisfyNeeds() {
        needs.forEach(Runnable::run);
    }
}

【讨论】:

  • 你能详细说明一下吗?每当我想重新排序时,我是否应该用所需的顺序写needs = Arrays.asList(() -&gt; eat(), () -&gt; sleep());?
  • @WVrock 您可以最初定义订单,然后在更改时重新排序。或者如果您将静态优先级值作为整数使用Map(请参阅链接)。也许你有“预设”,比如final List&lt;Runnable&gt; needsDuringDayTime = ....; final List&lt;Runnable&gt; needsDuringNightTime = ...,然后你将它们设置在satisfyNeeds():List&lt;Runnable&gt; needs = isDayTime() ? needsDuringDayTime : needsDuringNightTime。
  • @WVrock 或者想象一下,您有最后一餐、睡眠时间或电视会话的计数器或时间戳,因此需求会动态变化。您可以使用自定义Comparator 对List 进行排序,例如Collections.sort(needs, customComparator);
  • () -&gt; eat() 与 new Runnable() { public void run() { eat() } } 不同还是更便于输入?
  • @Random832 一样。我更新了我的答案,使其更易于阅读。
【解决方案4】:

您可以使用此代码

import java.util.Arrays;  // must be imported
int[] priorities = {sleepPriority, eatPriority, recreationPriority};
Arrays.sort(priorities);
for (int i=priorities.length-1; 0<=i; i--) {
    int priority = priorities[i];
    if (priority == sleepingPriority) { sleep(); }
    if (priority == eatingPriority) { eat(); }
    if (priority == recreationPriority) { watchTv(); }
}

基本上,它将优先级放在一个数组中,对数组进行排序并在其上运行一个 for 循环来运行函数。

【讨论】:

  • 需要 -1 到起始 i 值。导致 outOfBoundsException。 int i=priorities.length -1
  • 哦..我现在去看看。
  • 好的,我修好了。它现在应该可以工作了。正如@WVrock 指出的那样,我添加了-1。
  • 我已经对此进行了测试,并且可以正常工作。我认为这是最简单的解决方案。没有额外的类或接口。它甚至只使用一个导入。
  • @AntonDozortsev switch case 不适用于动态变量。
【解决方案5】:

找到三个元素的正确顺序可以像这样简单地完成:

 void satisfyNeeds() {
     boolean eatFirst = eatingPriority>Math.max(sleepingPriority,recreationPriority);
     if(eatFirst) eat();
     if(sleepingPriority>recreationPriority) {
         sleep();
         watchTv();
     }
     else {
         watchTv();
         sleep();
     }
     if(!eatFirst) eat();
  }

当然,如果您增加操作的数量,它不会扩展。对于更高的数字,您可以查看其他答案之一。

【讨论】:

    【解决方案6】:

    你应该在Person类中引入一个map属性,其中优先方法,例如:

    class Person {
    
    ...
    private Map<Integer, Method> methodsPriority = new HashMap<>();
    ...
     public Person setEatingPriority(int priority) {
    
      methodsPriority.put(priority, /* put 'eat' method reference here*/);
      return this;
     }
    
     public Person setSleepingPriority(int priority) {
    
      methodsPriority.put(priority, /* put 'sleep' method reference here*/);
      return this;
     }
    
     public Person setWatchingTVPriority(int priority) {
    
      methodsPriority.put(priority, /* put 'watch TV' method reference here*/);
      return this;
     }
    
     public void satisfyNeeds() {
    
      Collection<Integer> keys = methodsPriority.keySet();
      Collections.sort(keys);
      for(Integer key: keys)
       methodsPriority.get(key).invoke(this);
     }
    
    
    
    ...
    }
    

    它可以用在下一个方式:

    Person Anna = new Person()
    .setEatingPriority(1)
    .setSleepingPriority(2)
    .setWatchingTVPriority(3);
    
    Person Bob = new Person()
    .setEatingPriority(3)
    .setSleepingPriority(2)
    .setWatchingTVPriority(1);
    
    Anna.satisfyNeeds();
    Bob.satisfyNeeds();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      • 2010-10-26
      相关资源
      最近更新 更多