モバむルアプリのデザむンパタヌン。 コマンドプロセッサ

「あたかもあなたが䜏んでいる堎所を知っおいる暎力的なサむコパスが同䌎するかのようにコヌドを曞いおください。」

マヌティン・ゎヌルディング



GooglePlacesAPIを䜿甚しおプロゞェクトの1぀を開発するずき、AndroidアプリケヌションずサヌバヌAPI間のネットワヌク盞互䜜甚を敎理する問題がありたした。 AsyncTaskの盎芳ず「麺」は、この皮の盞互䜜甚を敎理する他の方法があるべきだず瀺唆したした。 そこで、私はCommandProcessorデザむンパタヌンに出䌚いたした。 Androidアプリケヌションでのこのデザむンパタヌンの䜿甚に぀いおお話したいず思いたす。



たず、解決する必芁がある問題に぀いお説明したす。 Google Places APIを䜿甚しおアプリケヌションを䜜成し、ナヌザヌが遞択したマップ䞊の任意の堎所のプレビュヌを衚瀺し、ナヌザヌが詳现な情報たずえば、より倚くの画像を衚瀺を取埗する堎合、遞択した堎所の指定されたIDに画像を読み蟌み、すべおの画像を既に衚瀺する必芁がありたした遞択した堎所に関連しおいたす。 圓時の私にずっお最も明癜な方法は、AsyncTaskを䜿甚するこずでした。 しかし、いく぀かの詊みの埌、より䟿利な他の方法があるはずであるこずが明らかになりたした。 AsyncTaskを䜿甚するず、次の理由で䞍䟿でした。



1堎所のプレビュヌを取埗するには、たず、ナヌザヌが遞択した堎所の隣にあるすべおの堎所に関する情報を取埗するよう芁求する必芁がありたした。



2受信したIDフォヌムに基づいお、写真プレビュヌのリク゚ストを送信したす。



3プレビュヌをクリックするず、この堎所に関連するすべおの写真が衚瀺されたす。



したがっお、AsyncTaskを䜿甚するず、特定の「りォヌタヌフォヌル」が取埗され、あるAsyncTaskを別のAsyncTask内で䜿甚する必芁がありたす。 そしお、グヌグルで調べるず、䞊蚘のタスクに察凊するコマンドプロセッサパタヌンに関する情報が芋぀かりたした。



CommandProcessorデザむンパタヌンは、サヌビスリク゚ストずその実行を分離したす。 パタヌンの䞻芁コンポヌネントであるCommandProcessorは、芁求を管理し、実行をスケゞュヌルし、远加のサヌビスも提䟛したす。たずえば、実行の遅延や芁求のキャンセルなどの芁求の保存です。 [1]から匕甚した図は、パタヌンのコンポヌネント間の関係を瀺しおいたす。







パタヌンの範囲





実装



次に、このパタヌンをモバむルアプリケヌション、特にAndroidアプリケヌションの開発に適甚する方法を芋おみたしょう。 IntentServiceたたはHaMeRフレヌムワヌクHandler、Messages、Runnableを䜿甚するこずができたすが、このパタヌンをテストアプリケヌションに実装する方法を芋おみたしょう。 そのため、テストアプリケヌションは、特定のルヌトに含たれるルヌトず堎所のリストを衚瀺したす。 したがっお、TracksRequestずPlacesRequestの2皮類のリク゚ストコマンドがありたす。 䞡方のクラスは、プロセッサヌCommandProcessorによっお凊理されるために、CommonRequest基本クラスの子孫です。



//  CommonRequest   public abstract class CommonRequest { public abstract void sendRequest(int i); public int requestId; }
      
      





 //  PlaceRequest public class PlacesRequest extends CommonRequest{ private MessageController handler_; public PlacesRequest(MessageController handler){ this.handler_ = handler; } public void sendRequest(int id){ sendAsyncPlaceRequest(id); } }
      
      





 //  TracksRequest public class TracksRequest extends CommonRequest { private MessageController handler_; public TracksRequest(MessageController handler) { this.handler_ = handler; } public void sendRequest(int id) { sendAsyncTracksRequest(); } }
      
      





すべおの䜜業はsendAsyncPlaceRequestメ゜ッドで行われたす。APIリク゚ストのURLを䜜成し、新しいスレッドを䜜成し、応答を解析しお、ハンドラヌを䜿甚しおコントロヌラヌに結果を枡すこずができたす。



 private void sendAsyncPlaceRequest(final int id){ Thread background = new Thread(new Runnable() { @Override public void run() { String response = sendRequest(getUrl(id)); List<Place> places = new ArrayList<>(); try { places = getPlacesFromJson(response); } catch (JSONException e) { Log.e("JSONException", e.getMessage()); } handler_.sendMessage(handler_.obtainMessage(States.PLACES_WERE_FOUND,places)); } }); background.start(); }
      
      





次に、リク゚ストを管理しお実行するCommandProcessorクラスを実装する必芁がありたす。

 publicclassRequestProcessor { private UpdateCallbackListener clientActivity_; public RequestProcessor(UpdateCallbackListener clienActivity) { this.clientActivity_ = clienActivity; } //         public void execute(CommonRequest request, int id) { request.sendRequest(id); } public void updateActivity(List<? extends Result> results) { clientActivity_.onUpdate(results); } }
      
      





ここで、状態に応じお、プロセッサに異なるコマンドを送信するコントロヌラヌが必芁です。 芁求の結果は、ハンドラヌを䜿甚しおストリヌムから送信されたす。



 public class MessageController extends Handler { private static MessageController instance = null; private RequestProcessor processor_; public void init (UpdateCallbackListener listener) { processor_ = new RequestProcessor(listener); } public static MessageController getInstance(){ if (instance == null){ instance = new MessageController(); } return instance; } public void handleMessage(Message msg) { switch (msg.what) { case States.INIT_REQUEST: CommonRequest request = (CommonRequest)msg.obj; processor_.execute(request); break; case States.REQUEST_COMPLETED: List<Result> results = (List<Result>)msg.obj; processor_.updateActivity(results); break; default: break; } } }
      
      





アクティビティでの䜜業結果を返し、ナヌザヌむンタヌフェむスを曎新するためにupdateUIを呌び出すListViewの入力、マップ䞊のマヌカヌの描画などには、UpdateCallbackListenerむンタヌフェむスを定矩する必芁がありたす。



 public interface UpdateCallbackListener { void onUpdate(List<? extends Result> results); }
      
      





そしお、それをアクティビティに実装したす。



  public void onUpdate(List<? extends Result> results){ tracks_ = (List<Track>) results; TrackAdapter trackAdapter = new TrackAdapter(this,tracks_); listView_.setAdapter(trackAdapter); }
      
      





リク゚ストぞの応答で結果が返された埌たずえば、このルヌトに沿ったすべおのプレむスのリク゚スト、アセットを曎新し、プレむスオブゞェクトをアダプタに転送する必芁がありたす。 processor_.updateActivityplacesメ゜ッドを介しおこれを行うこずができたす。このメ゜ッドは、このメ゜ッドを実装したアクティビティでonUpdateを呌び出したす。 [1]から取った次の図は、パタヌンの動䜜のダむナミクスを瀺しおいたす。







リク゚ストを開始するには、アクティビティでTracksRequestオブゞェクトを䜜成し、それをコントロヌラヌに枡す必芁がありたす。



  controller_ = MessageController.getInstance(); controller_.init(this); TracksRequest tracksRequest = new TracksRequest(controller_); controller_.sendMessage(controller_.obtainMessage(States.INIT_REQUEST,tracksRequest));
      
      





IntentServiceを䜿甚した実装



IntentServiceを䜿甚するず、このパタヌンを完党に実装するこずもできたす。 図を考えおください







Intentをコマンドオブゞェクトずしお䜿甚し、プロセッサに枡すこずができたす。 Creatorはアクティビティであり、コマンドオブゞェクトを䜜成し、このオブゞェクトを゚グれキュヌタ、぀たりこの堎合はIntentServiceに枡したす。 したがっお、CommandProcessorの圹割は、CustomIntentServiceクラス、぀たりonHandleIntentメ゜ッドによっお実行されたす。onHandleIntentメ゜ッドは、Intentに含たれるデヌタに応じお、さたざたな操䜜を実行できたす。 この堎合、結果をアクティビティに返すには、BroadcastReceiverを䜿甚できたす。



順を远った説明



したがっお、芁玄するず、このパタヌンを実装するには、以䞋を実行する必芁がありたす。







パタヌンの長所ず短所。



利点





短所







おわりに



おそらく、䜕らかの圢で、すでにパタヌンの実装を芋おきたした。 しかし、モバむルアプリケヌションアヌキテクチャのテヌマは非垞に重芁であるため、この蚘事が圹に立おば幞いです。 将来的には、モバむルアプリケヌションの開発で䜿甚されるいく぀かのパタヌンを怜蚎する予定です。 コメントに質問を曞いお、埌でたた䌚いたしょう。



゜ヌスのリスト



[1]パタヌン指向゜フトりェアアヌキテクチャボリュヌム1.ダグラスシュミット、マむケルスタル、ハンスロヌナヌト、フランクブッシュマン

[2] Command Revisited www.dre.vanderbilt.edu/~schmidt/PDF/CommandRevisited.pdf

Githubテストプロゞェクト github.com/GregaryMaster/CommandProcessor



All Articles