2014-04-18 140 views
0

這實際上是一個半問題,半討論帖。 我覺得需要在Java中的特徵是,方法(可以說「calcTotalX」)可通過在一類(即ParallelExecuter),這將執行的另一方法的beforeStart/AfterEnd註解來限定(可以說doJob)在同一類。因此,我們可以確定,任何擴展ParallelExecuter的類(可以說SortingParallelExecuter)並重寫其「doJob」方法不必知道X,可能會遺忘處理X,處理有關X的操作事件等。Java繼承和OOP和AOP

My問題是,Java中有沒有什麼可以像這樣做,除了AOP。 我不選擇AOP,因爲它會使代碼分佈太多而且難以閱讀。 另外這裏關注的是類/方法/屬性的具體情況。因此其他類/方法/屬性不需要複製行爲。

順便說一句,如果你發現這個合理的,請投票支持線程。日Thnx

好,爲是具體的我加入,我用分割& paralelization樣本類。

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()必須以AOP方式交叉切割爲「doJob」方法。是的,我們可以在另一個類中實現此功能,但任何擴展PartitionedParallelExecutor的類都可以擴展此AOP行爲,正如我所知。在這一點上,我問,爲什麼這些東西(計算「totalExecutedJobCount」,onPerPartitionFail())必須在另一個類。它們與這個類,它的屬性和方法有關。以面向對象的方式來看,它們必須在同一個類中有一些方法,並在「doJob」結束時調用。就是那個問題。現在希望事情很清楚。 Thnx你的時間。

+1

您對AOP的偏見沒有任何意義。我建議你先告訴你自己並在你說它「難以閱讀」之前先試用它,因爲這不正確。分散式?不,相反:方面代碼很好地模塊化。它很容易閱讀,因爲它是乾淨的,沒有橫切關注的核心代碼。 – kriegaex

+0

thnx您的評論,但我已經使用Spring AOP。 –

+0

Spring AOP是「AOP light」,只是在Spring Beans上攔截方法。雖然語法等同於AspectJ,但後者有更多的可能性,並且效率更高,因爲它不需要任何動態代理。它適用於非Spring Bean(即普通的POJO),可以攔截成員變量的讀/寫操作,構造函數調用,靜態類初始化,catch塊等等。但最重要的是沒有容易出錯的基於字符串的註釋風格的本地語法。 – kriegaex

回答

1

我們的意見經過討論,閱讀彼得的回答您的評論,並檢查您最近添加的示例代碼,其實我明白你的問題,但不明白爲什麼你看到問題在那裏。

您的方法fireAndWait()實際上是一個Template Method,這是一個成熟的OOP設計模式。我認爲將doWork(List<T>)作爲算法的一部分來執行是完全正確的,但是指示用戶(通過JavaDoc)不要自己調用它,而是要依靠間接調用它。舉例來說,你經常實施Runnable.run()(甚至在你的示例代碼!),但是不要抱怨它沒有被自己通過Thread.start()ExecutorService.execute()稱爲而是間接的。那不一樣的模式?爲什麼一個「上帝的方法」會做所有事情?

如果你不喜歡自己的模式方法的方法,隨便寫一個PartitionedParallelExecutorStatisticsAspect(遺憾的長文件名)取本方面的護理(因爲這是它是什麼)。如果你願意的話,把它放在同一個包裏,所以它就在抽象類的附近,讓它做它的工作。上帝的課程與上帝的方法一樣糟糕,所以可以說使用AOP也是一個可行的選擇。做(平行分區)工作是這裏的核心問題,保持統計是次要的。如果幹淨地實施,我會對這兩種方法都很好。

雖然這個話題有點哲學性,而且我在這裏看到了幾個近距離投票,這是可以理解的,但我仍然希望我的評論有幫助。如果是這樣,請隨時接受答案並關閉該主題,以免使其成爲無盡的討論主題。

+0

你的解釋和Runnable.run()的例子足以接受我。如果可以做得更好,我只是試圖尋找。但似乎並不是那麼大的問題。我沒有注意到和理解的一點是解決問題的壓力。沒有人甚至告訴過我這件事。這似乎是一個無聊和非實際的概念問題,但有助於學習和設計。那麼爲什麼人們打擾呢?感謝Thanx。 –

1

也許你應該使用這樣的代碼:

public final void doJob() { 
    before(); 
    inside(); 
    after(); 
} 

    protected void inside() { 
} 

private void before() { 
} 

private void after() { 
} 

現在你不能超載doJob(),但只有()方法內,你有攔截器:前()和()之後。

+0

是的,這似乎是一個合理的替代解決方案,直到新功能。我唯一反對的是,擴展這個類的程序員必須意識到重寫「內部」方法,但是調用「doJob」而不是「內部」,這使得它容易出錯。 thnx爲你的答案 –