这实际上是一个半问题,半讨论的话题。
  我认为Java需要一个功能,即可以通过类(即ParallelExecuter)中的注释定义方法(可以说“ calcTotalX”),该类将在同一类中的另一个方法(可以说doJob)的Start / AfterEnd之前执行。因此,我们可以确保任何扩展ParallelExecuter并覆盖其“ doJob”方法的类(让我们说SortingParallelExecuter)都不必了解X,不必冒着处理X的风险,无需处理有关X的操作事件,等等。

我的问题是,除了AOP之外,Java中有什么可以做到的呢?
  我之所以不选择AOP,是因为它会使代码变得如此分散且难以阅读。
  同样,这里的关注点是类/方法/属性特定的。因此,其他类/方法/属性不需要复制行为。

顺便说一句,如果您认为合理,请对该主题投票。 n

好的,为具体起见,我添加了一个示例类,用于划分和并行化。

public abstract class PartitionedParallelExecutor<T> {

private ExecutorService executorService;

private final List<PartitionErrorDesc<T>> errorMap     = new ArrayList<PartitionErrorDesc<T>>();
private final AtomicInteger totalExecutedJobCount      = new AtomicInteger();

private boolean shutdownForced = false;


private final int workerCount;
private final int partitionCount;
protected final List<T> sourceList;

//Must be implemented via Extender class
protected abstract PartitionErrorDesc<T> doWork(List<T> subList);



public PartitionedParallelExecutor(int workerCount, int partitionCount, List<T> sourceList) {
    super();
    this.workerCount = workerCount;
    this.partitionCount = partitionCount;
    this.sourceList = sourceList;
}



public Object onPerPartitionFail(List<T> subList, PartitionErrorDesc<T> ped){return null;};

public Object onPerPartitionSuccess(List<T> subList){return null;};

public Object onAnyFailDoOnce() {return null;}

public Object onTotalSuccess() {return null;}


public final void  fireAndWait() {

    if(workerCount <= 0 || partitionCount <= 0 ||
            sourceList == null || sourceList.size() == 0){
        throw new IllegalArgumentException();
    }

    ExecutorService executorService = Executors.newFixedThreadPool(workerCount);
    this.executorService = executorService;

    List<List<T>> partitions = partitionList(sourceList, partitionCount);

    for (final List<T> subList : partitions) {

        executorService.execute(new Runnable() {
                                    @Override
                                    public void run() {

                                        PartitionErrorDesc<T> errorDesc = null;

                                        try {
                                            errorDesc = doWork(subList);
                                        } catch (Throwable e) {

                                            errorDesc = new PartitionErrorDesc<T>(subList);
                                            errorDesc.setSuccess(false);
                                            errorDesc.setE(e);
                                            errorDesc.setFailedAtItem(0);
                                        }

                                        errorMap.add(errorDesc);

                                        if(errorDesc.isSuccess == false) { //failure

                                            onPerPartitionFail(subList, errorDesc);
                                            setShutdownForced(true);

                                            totalExecutedJobCount.addAndGet(errorDesc.getFailedAtItem());
                                            Thread.currentThread().interrupt();
                                            return;
                                        } else { //success
                                            totalExecutedJobCount.addAndGet(subList.size());
                                            onPerPartitionSuccess(subList);
                                        }
                                    }
        });
    }

    executorService.shutdown();

    try {
        executorService.awaitTermination(60, TimeUnit.MINUTES);
    } catch (InterruptedException e) {

        setShutdownForced(true);
        Thread.currentThread().interrupt();
    }

    if (!isShutdownForced()) {
        onTotalSuccess();
    } else {
        onAnyFailDoOnce();
    }
}


private List<List<T>> partitionList(List<T> sourceList , int partitionCount) {
    List<List<T>> partitions = new ArrayList<List<T>>();
    int totalSize = sourceList.size();

    int pageCount = partitionCount;
    int pageSize  = totalSize / pageCount;
    int remainder = totalSize % (pageSize * pageCount);

    int fromIndex  = 0;
    int toIndex  = 0;
    for(int i = 0;  i < pageCount; i++) {

        fromIndex = toIndex;

        if(toIndex >= totalSize){
            break;
        }

        if ( remainder > i) {
            toIndex = toIndex + pageSize + 1;
        } else {
            toIndex = toIndex + pageSize;
        }

        List<T> subList = sourceList.subList(fromIndex,toIndex);

        partitions.add(subList);
    }

    return partitions;
}

public final void shutdownNow() {
    setShutdownForced(true);
    List<Runnable> runnables = executorService.shutdownNow();

    try {
        if(!executorService.awaitTermination(60,TimeUnit.SECONDS)) {
            LOG.error("pool didnt terminate after 60 seconds in shutdownNow");
        }
    } catch (InterruptedException e) {
        executorService.shutdownNow();
        Thread.currentThread().interrupt();
    }
}

public final boolean isShutdownForced() {
    return shutdownForced;
}

private synchronized void setShutdownForced(boolean shutdownForced) {
    this.shutdownForced = shutdownForced;
}


}

在此示例中,想要使用上述类以多线程方式完成其工作的程序员,他必须实现“ doJob()”,但调用“ fireAndWait()”。实现doJob并调用doJob将是一种更简洁的方法。诸如计算“ totalExecutedJobCount”,onPerPartitionFail()之类的其余事情必须以交叉于“ doJob”方法的AOP方式实现。是的,我们可以在另一个类中实现此功能,但是据我所知,任何扩展PartitionedParallelExecutor的类也可以扩展此AOP行为。在这一点上,我问为什么这些东西(计算“ totalExecutedJobCount”,onPerPartitionFail())必须在另一个类中。它们与该类,其属性和方法有关。以面向对象的方式查看,它们必须在同一类中包含一些方法,并在“ doJob”结束时进行调用。就是那个问题。希望现在一切都清楚了。谢谢你的时间。

最佳答案

在讨论我们的评论之后,阅读了您对Peter的回答的评论并检查了您最近添加的示例代码,我实际上理解了您的问题,但是不明白为什么您在那里看到问题。

您的方法fireAndWait()实际上是Template Method,这是一种成熟的OOP设计模式。我认为将doWork(List<T>)作为算法的一部分来实现是完全可以的,但是可以指示用户(通过JavaDoc)不要自己调用它,而是依靠间接调用它。例如,您经常实现Runnable.run()(即使在示例代码中也是如此!),但不要抱怨它不是您自己调用的,而是通过Thread.start()ExecutorService.execute()间接调用的。这不是相同的模式吗?为什么一个“上帝方法”应该做所有事情?

如果您不喜欢自己的模式方法,请随意编写PartitionedParallelExecutorStatisticsAspect(长名称表示抱歉),以解决这一方面的问题(因为这就是问题所在)。如果愿意,可以将其放入同一包中,这样它就靠近抽象类,并让它完成工作。上帝的课程和上帝的方法一样糟糕,因此可以说使用AOP也是可行的选择。做(并行分区)工作是这里的核心问题,保持统计数据是次要的。如果干净利落地使用这两种方法,我都很好。

尽管该主题在某种程度上是哲学上的,并且我在这里看到了几张近距离投票,这是可以理解的,但我仍然希望我的评论有所帮助。如果是这样,请随时接受答案并关闭主题,以免使其成为无休止的讨论话题。

07-26 04:25