Javaにおけるコンポジットビューパターン:アプリケーション全体でUIの一貫性を高める
コンポジットビューデザインパターンの目的
コンポジットビューデザインパターンの主な目的は、オブジェクトをツリー構造に構成して、部分-全体の階層構造を表現することです。これにより、クライアントは個々のオブジェクトとオブジェクトの構成を統一的に扱うことができ、複雑な階層ビューの管理が簡素化されます。
実際の例を用いたコンポジットビューパターンの詳細な説明
実際の例
コンポジットビューデザインパターンの現実世界の例は、Webアプリケーションのダッシュボードのレイアウトです。株価チャート、最近の取引、口座残高、ニュースフィードなどのさまざまなウィジェットを表示する財務ダッシュボードを考えてみましょう。これらの各ウィジェットは、個別に更新および管理できる独立したビューコンポーネントです。コンポジットビューパターンを使用することにより、これらの個々のウィジェットは単一の統合されたダッシュボードビューに構成されます。このアプローチにより、ダッシュボードの簡単な再編成、既存のウィジェットを中断することなく新しいウィジェットの追加、および全体的なレイアウトの一貫した管理が可能になります。ビューのこの階層的な構成は、ダッシュボードのさまざまなセクションが個々のエンティティとして、またより大きな全体の一部としてどのように扱われるかを反映しています。
平易な言葉で
コンポジットビューパターンとは、小さなサブビューで構成されるメインビューを持つことです。このコンポジットビューのレイアウトは、テンプレートに基づいています。次に、ビューマネージャーがこのテンプレートに含めるサブビューを決定します。
Wikipediaによると
複数のアトミックサブビューで構成されるコンポジットビュー。テンプレートの各コンポーネントは、全体に動的に含めることができ、ページのレイアウトはコンテンツとは独立して管理できます。このソリューションは、モジュール式の動的および静的テンプレートフラグメントの包含と置換に基づいて、コンポジットビューを作成します。これにより、モジュール設計を促進することにより、ビューのアトミック部分の再利用が促進されます。
Javaでのコンポジットビューパターンのプログラム例
あるニュースサイトは、ユーザーの好みに基づいて、現在の日付とニュースをさまざまなユーザーに表示したいと考えています。ニュースサイトは、ユーザーの関心に応じてさまざまなニュースフィードコンポーネントを代替し、デフォルトではローカルニュースになります。
これはWeb開発パターンであるため、それを実証するにはサーバーが必要です。この例では、サーブレットを実行するためにTomcat 10.0.13を使用しており、このプログラム例はTomcat 10以降でのみ機能します。
まず、Tomcat 10以降で実行されるHttpServlet
であるAppServlet
があります。
public class AppServlet extends HttpServlet {
private String msgPartOne = "<h1>This Server Doesn't Support";
private String msgPartTwo = "Requests</h1>\n"
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
private String destination = "newsDisplay.jsp";
public AppServlet() {
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
req.setAttribute("properties", reqParams);
requestDispatcher.forward(req, resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println(msgPartOne + " Post " + msgPartTwo);
}
@Override
public void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println(msgPartOne + " Delete " + msgPartTwo);
}
@Override
public void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println(msgPartOne + " Put " + msgPartTwo);
}
}
このサーブレットはパターンの一部ではなく、GETリクエストを正しいJSPに転送するだけです。PUT、POST、およびDELETEリクエストはサポートされておらず、エラーメッセージが表示されます。
この例でのビュー管理は、ユーザーのプリファレンスを格納するjavabeanクラスであるClientPropertiesBean
を介して行われます。
public class ClientPropertiesBean implements Serializable {
private static final String WORLD_PARAM = "world";
private static final String SCIENCE_PARAM = "sci";
private static final String SPORTS_PARAM = "sport";
private static final String BUSINESS_PARAM = "bus";
private static final String NAME_PARAM = "name";
private static final String DEFAULT_NAME = "DEFAULT_NAME";
private boolean worldNewsInterest;
private boolean sportsInterest;
private boolean businessInterest;
private boolean scienceNewsInterest;
private String name;
public ClientPropertiesBean() {
worldNewsInterest = true;
sportsInterest = true;
businessInterest = true;
scienceNewsInterest = true;
name = DEFAULT_NAME;
}
public ClientPropertiesBean(HttpServletRequest req) {
worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
String tempName = req.getParameter(NAME_PARAM);
if (tempName == null || tempName == "") {
tempName = DEFAULT_NAME;
}
name = tempName;
}
// getters and setters generated by Lombok
}
このjavabeanには、デフォルトコンストラクターと、HttpServletRequest
を受け取る別のコンストラクターがあります。
この2番目のコンストラクターはリクエストオブジェクトを受け取り、さまざまな種類のニュースに対するユーザーのプリファレンスを含むリクエストパラメーターを解析します。
ニュースページのテンプレートはnewsDisplay.jsp
にあります。
<html>
<head>
<style>
h1 {
text-align: center;
}
h2 {
text-align: center;
}
h3 {
text-align: center;
}
.centerTable {
margin-left: auto;
margin-right: auto;
}
table {
border: 1px solid black;
}
tr {
text-align: center;
}
td {
text-align: center;
}
</style>
</head>
<body>
<%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
<h1>Welcome <%= propertiesBean.getName()%></h1>
<jsp:include page="header.jsp"></jsp:include>
<table class="centerTable">
<tr>
<td></td>
<% if(propertiesBean.isWorldNewsInterest()) { %>
<td><%@include file="worldNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
</tr>
<tr>
<% if(propertiesBean.isBusinessInterest()) { %>
<td><%@include file="businessNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
<% if(propertiesBean.isSportsInterest()) { %>
<td><%@include file="sportsNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
</tr>
<tr>
<td></td>
<% if(propertiesBean.isScienceNewsInterest()) { %>
<td><%@include file="scienceNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
</tr>
</table>
</body>
</html>
このJSPページがテンプレートです。3つの行を持つテーブルが宣言されており、最初の行に1つのコンポーネント、2番目の行に2つのコンポーネント、3番目の行に1つのコンポーネントがあります。
ファイル内のスクリプトレットは、Javabeanのユーザープリファレンスに基づいてさまざまなアトミックサブビューを含めるビュー管理戦略の一部です。
コンポジットで使用されるモックアトミックサブビューの2つの例を次に示します:businessNews.jsp
<html>
<head>
<style>
h2 {
text-align: center;
}
table {
border: 1px solid black;
}
tr {
text-align: center;
}
td {
text-align: center;
}
</style>
</head>
<body>
<h2>
Generic Business News
</h2>
<table style="margin-right: auto; margin-left: auto">
<tr>
<td>Stock prices up across the world</td>
<td>New tech companies to invest in</td>
</tr>
<tr>
<td>Industry leaders unveil new project</td>
<td>Price fluctuations and what they mean</td>
</tr>
</table>
</body>
</html>
localNews.jsp
<html>
<body>
<div style="text-align: center">
<h3>
Generic Local News
</h3>
<ul style="list-style-type: none">
<li>
Mayoral elections coming up in 2 weeks
</li>
<li>
New parking meter rates downtown coming tomorrow
</li>
<li>
Park renovations to finish by the next year
</li>
<li>
Annual marathon sign ups available online
</li>
</ul>
</div>
</body>
</html>
worldNews.jsp
、businessNews.jsp
などのさまざまなサブビューは、リクエストパラメーターに基づいて条件付きで含まれます。
使い方
この例を試すには、Tomcat 10以降がインストールされていることを確認してください。IDEを設定して、モジュールからWARファイルをビルドし、そのファイルをサーバーにデプロイします。
IntelliJ
Run
およびedit configurations
で、Tomcatサーバーが実行構成の1つであることを確認してください。デプロイタブに移動し、composite-view:war exploded
という1つのアーティファクトがビルドされていることを確認してください。存在しない場合は、追加します。
アーティファクトがweb
ディレクトリの内容とモジュールのコンパイル結果からビルドされていることを確認します。アーティファクトの出力を便利な場所にポイントします。構成を実行してランディングページを表示し、そのページの指示に従って続行します。
Javaでコンポジットビューパターンを使用する場合
コンポジットビューデザインパターンは、次の場合に使用します。
- オブジェクトの部分-全体の階層を表現したい場合。
- コンポジット構造に将来新しいコンポーネントが含まれる可能性がある場合。
- クライアントがオブジェクトの構成と個々のオブジェクトの違いを無視できるようにしたい場合。クライアントは、コンポジット構造内のすべてのオブジェクトを統一的に扱います。
コンポジットビューパターンJavaチュートリアル
Javaにおけるコンポジットビューパターンの実際のアプリケーション
- ウィジェットが他のウィジェットを含むことができるグラフィカルユーザーインターフェース(GUI)(例:パネル、ボタン、テキストフィールドを含むウィンドウ)。
- 行を含むテーブル、さらにセルを含むテーブルなど、すべてが統一された階層の要素として扱われるドキュメント構造。
コンポジットビューパターンの利点とトレードオフ
利点
- 新しいコンポーネントを追加する際の高い柔軟性:コンポジットノードとリーフノードは統一的に扱われるため、新しい種類のコンポーネントを簡単に追加できます。
- 簡素化されたクライアントコード:クライアントはコンポジット構造と個々の要素を統一的に扱うことができるため、クライアントコードの複雑さが軽減されます。
トレードオフ
- 過剰な一般化:特にアプリケーションで必要ない場合は、すべてをコンポジットにすると、システムの設計がより複雑になる可能性があります。
- 制約の適用の難しさ:コンポジットのコンポーネントを特定のタイプのみに制限することがより困難になる場合があります。
関連するJavaデザインパターン
- コンポジット:個々のオブジェクトと構成を統一的に扱うために使用されるコンポジットビューの基礎となる一般的な構造パターン。
- デコレーター:基になるビューを変更せずに、個々のビューの動作を強化します。
- フライウェイト:多数の類似したビューオブジェクトのメモリ消費を管理するために使用できます。
- ビューヘルパー:ビューロジックをビジネスロジックから分離し、ビューコンポーネントのクリーンな編成と管理を支援します。