2017-06-20 30 views
1

我正在用服務器構建一個使用Java的多人遊戲。目前,我正在使用單個類文件來存儲播放器數據並處理數據。我是初學者,所以我不知道這是一個不好的習慣。 http://howtodoinjava.com/best-practices/5-class-design-principles-solid-in-java/這篇文章幫助我理解我打破了「單一責任原則」的規則。如何在java中分離數據和行爲對象?

這就是我的代碼現在的樣子。

public class PlayerSession{ 

    String playerId; 
    String playerName; 

    // 20+ player data fields, which I am trying to reduce 
    // and keep only the most used data 

    public void messageProcessor(JSONObject clientRequest) throws JSONException{ 

     switch(clientRequest.getString("task")){    
     case "login": loginProcess(); break; 
     case "logout": logoutProcess(); break; 

     //50+ different actions 
     } 
    } 

    public void populateSessionData(String playerId){ 
     // populate player data from database 
    } 

    private void loginProcess(){ 
     //Process login 
    } 

    private void logoutProcess(){ 
     //Process logout 
    } 

    //20+ other methods which do entirely different tasks. 
} 

隨着我們添加更多功能,該類將變得極其難以維護和修改。現在我正試圖將這個類分成兩個不同的類。一個是存儲玩家數據,另一個是處理行爲,如下所示。

public class PlayerSession { 

    final TaskHandler taskHandler = new TaskHandler(); 

    public void messageProcessor(JSONObject clientRequest) throws JSONException { 

     switch (clientRequest.getString("task")) { 
     case "login": 
      taskHandler.loginProcess(); 
      break; 
     case "logout": 
      taskHandler.logoutProcess(); 
      break; 

     // 50+ different actions 
     } 
    } 
} 

public class PlayerData { 

    String playerId; 
    String playerName; 

    // 20+ player data fields, which I am trying to reduce 
    // and keep only the most used data 

    public void populateSessionData(String playerId) { 
     // populate player data from database 
    } 
} 

public class TaskHandler { 

    final PlayerData player = new PlayerData(); 

    private void loginProcess() { 
     // Process login 
    } 

    private void logoutProcess() { 
     // Process logout 
    } 

    // 20+ other methods which do entirely different tasks. 
} 

而且這個設計爲單個客戶端創建了2個額外的對象,即PlayerData和TaskHandler。對於10,000個併發客戶端的服務器,這會成爲一個問題嗎?這是做到這一點的正確方法嗎?如果不是,這樣的場景的最佳方法是什麼?

某處我讀到的對象只是爲了保存數據不是一個好方法。是對的嗎?

+0

你是否在使用像spring這樣的框架? –

+0

@AshutoshJha我正在使用netty和websockets。這不是基於REST的應用程序。這是一個需要全雙工,實時通信的Web應用程序。 –

回答

0

你需要在這裏做了很多事情:

PlayerSession類:

爲什麼你解析JsonObject在這裏?您需要創建名爲ClientRequest的課程,並且所有工作都將在其中完成。把所有你需要的從客戶端請求到這個類,只有那些將準備好使用的方法不要從對象中提取數據並在代碼中手動計算,這是程序化的方式。還將PlayerSession重命名爲Session

PlayerData類

首先其重命名爲Player類是代表球員沒有播放器的數據。不要在播放器中執行任何數據庫關係操作。同樣在創建之後,您需要準備好使用Player,在此創建實例,然後在其中填充數據,最好在構造函數中完成。

TaskHandler類

不要創建類,做很多事,順便說一句,你幾乎在正確的方式,簡單的創建界面TaskAction,這將是隻有一個方法performTask(),創造許多實現爲LoginTask, LogoutTask等。也許你需要有結構來給你實現特定行爲的實例,這也有助於擺脫手動創建具體實現,並且你將以更多樣化的方式來做到這一點。

public class Session { 

    private final SessionActions actions; 
    private final RequestFabric requests; 
    private final Player player; 

    public Session (SessionActionFabric actionsFabric, 
        RequestFabric requests, Player player) { 
     this.actionsFabric = actionsFabric; 
     this.requests = request; 
     this.player = player; 
    } 

    void login() throws LoginException { 
     Request request = request.createRequest(); 
     SessionAction login = actions.createActions("login", player, request); 
     login.performAction(); 
     //something like that it's depends on whole picture of your project and maybe changed 
    } 

    //etc. 
} 

public interface Request { 

    public performRequest(Player player, /*request data your api specs here*/) throws RequestException; 
} 

public class Player { 

    private String id; 
    private String name; 

    public Player(String id, String name){ 
     this.id = id; 
     this.name = name; 
    } 

    //getters, setters 
} 

interface SessionAction { 

    void performAction() throws SessionActionException; 
} 

class LoginAction implements SessionAction { 
    private final Player player; 
    private final Request request; 


    LoginAction (Player player, Request request) { 
     this.player = player; 
     this.request = request; 
    } 

    void performAction() { 
     // do you things here 
    } 

} 

Q對於10,000個併發客戶端的服務器,這會成爲一個問題?這是做到這一點的正確方法嗎?如果不是,這樣的場景的最佳方法是什麼?

A不要介意性能最好關注優秀的設計,如果你有性能方面的問題,你幾乎總能找到改進它的方式來改進它,好設計(緩存,池化等)。)

問題某處我讀到只是爲了保存數據的對象不是一個好方法。是對的嗎?

A你是righ,這叫貧血模型,(數據和方法來處理它是分開的),但在目前它非常受歡迎。

+0

我喜歡這種方法,我正在考慮設計類似於此的東西。但是我想避免太多的對象創建。客戶端每2秒會有請求。因此,對於單個客戶端,每兩秒創建一個操作對象將很快導致OutOfMemoryException。糾正我,如果我錯了。 –

+0

客戶端和服務器通信通過JSON進行。這就是爲什麼JSONObject。問題中使用的所有命名都是爲了更好地理解讀者。關於Player類,你是對的。我必須在構造函數中填充數據。 –

+0

@KaleshKaladharan你需要更好地理解java是如何工作的,特別是你需要閱讀更多關於垃圾收集的內容。在2秒內創建一個對象完全沒有。當在方法中創建對象時,使用它並從方法中退出收集此對象的垃圾,OfMemoryException不會拋出。附:如果您接受答案,則將其標記爲已接受。 – fxrbfg

0

根據您與Joy的討論。如果你擔心像LoginAction這樣的Action類會每2秒創建一次。使用單例模式,擁有動作類的靜態最終實例和該實例的一個靜態獲取器(LoginAction.getInstanceOfLoginAction()),並且每次都使用它,而不是創建新的實例。但要確保你的動作類是無狀態的,不是有狀態的!

class LoginAction implements SessionAction { 
    private static final LoginAction loginAction = new LoginAction(); 
    public static LoginAction getLoginAction() { 
     return loginAction; 
    } 

    void performAction(Player player, Request request) { 
    // do you things here 
    } 

}

一件事使用一個工廠(見工廠模式)爲獲得執行行動的接口按要求。

+0

https://dzone.com/articles/stateful-or-stateless-classes檢查無狀態和有狀態的鏈接一次。 –

+0

但是,當100個併發客戶端嘗試訪問該對象時會發生什麼情況。 LoginAction應該能夠使用令牌來驗證客戶端並在其各自的sessionData對象上填充數據。你可以請示例代碼來做到這一點。謝謝。 –

+0

我寫入LoginAction的方式沒有私人字段,即使有100個併發客戶端,它也能按預期工作。現在,如果你想在不同的線程上發送它們,那麼我想知道你是否在使用類似Future的java apis,並且還使用了RxJava(對於並行化來說非常棒)。 –