macbookのキーボードが壊れる場合の対策
エンターキーとかが壊れたら代用のキーを割り当てればいいかも
https://www.chiphell.com/thread-678136-1-1.html
- Karabiner
デザインパターン - Singleton
このクラス図で注目すべきことは以下の3点である。
- 同じ型のインスタンスが private なクラス変数として定義されている。
- コンストラクタの可視性が private である。
- 同じ型のインスタンスを返す
getInstance()
がクラス関数として定義されている。
クラス図内にあるアンダーラインは、その項目がクラス変数あるいはクラス関数であることを意味している。
Javaでの実装例[編集]
以下にSingleton パターンを用いたクラスのJavaによる例を示す。
final class Singleton {
private static Singleton instance;
private Singleton(){};
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
このクラスにおいて、コンストラクタはprivate
で定義されているため、他のクラスによって、Singleton
クラスのインスタンスを生成することはできない。このクラスのインスタンスを生成したいときは、getInstance()
メソッドを利用することになるが、このメソッドは最初に呼び出されたときにだけインスタンスを生成し、2回目以降に呼び出されたときは最初に生成したインスタンスを返すように作られている。そのため、プログラム中にSingleton
クラスのインスタンスが1つしか存在しないことが保証される。
getInstance()
メソッドがsynchronized
に指定されているのは、複数のスレッドからほぼ同時に呼び出された際に複数のインスタンスが生成されてしまう危険性をなくすためである。
問題点および改善策[編集]
Java における一般的な Singleton パターンの記述は上述した通りであるが、この方法は同期化コストが高い。そこでdouble-checked lockingというイディオム [2]が考えられたが、「アウトオブオーダー書き込み」を許すJavaプラットフォームのメモリー・モデルにより同期化に失敗する(言うまでもないが、この最適化による副作用は Java だけのものではない)。この問題を克服しようとするとコードが肥大化しコストがかかる。そこで現在では以下のように static フィールドを用いることが推奨されている。
final class Singleton {
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return Singleton.instance;
}
}
これによりコストは改善される。同期化は行われないが、static フィールドの初期化はそのクラスが呼び出される最初の一回しか行われないため、何回getInstance()
メソッドを呼んでもスレッドアンセーフを心配する必要はなくなるだけでなく、コストパフォーマンスも非常に高い。
ただしこの場合、Singletonクラスがロードされたときに初期化されるのであって、getInstance()
が初めて呼ばれたときではない。このことはプログラマーの意図しないタイミングで初期化が始まってしまい混乱の元となる場合がある。 そこで en:Initialization-on-demand holder idiom と呼ばれる手法、すなわちinstanceフィールドのみを別のホルダークラスに隔離して、そのホルダークラスがロードされたときにSingletonの初期化がおこわれるよう改善したものが下記である。
final class Singleton {
private Singleton(){}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
これでSingletonクラスがロードされたときではなく、getInstance()
呼び出しによりSingletonHolderクラスがロードされたときにSingletonクラスが初期化される。
デザインパターン - Facade
把复杂的调用处理集中到Facade类中,对外仅提供facade的简单的接口,避免过多的不必要的参数及方法的暴露,让调用侧更方便使用。
Facade (ファサード)
→ | イントロダクション |
→ | パターン解説 |
→ | 具体的な例 |
→ | まとめ |
イントロダクション
みなさんのサーブレット(Strutsを使用している場合はアクションクラス)の行数は、平均どれくらいでしょうか? データベースアクセスや業務処理など、すべての処理をサーブレットに詰め込もうとして、あっという間に1000行を越すような「太ったサーブレット」を作ってしまったことありませんか? サーブレットを初めて書いたときは筆者もそうでした。
このような「長く」「すべての処理が入った」サーブレットのことをすべてのことを行う魔法のようなサーブレットということで「マジックサーブレット」と呼びます。 マジックサーブレットは保守や機能拡張が難しいのはもちろんのこと、「アプリケーションが提供する機能」を把握することが難しくなるという弊害があります。 機能を把握できないと「あの機能ってどこにあったっけ?」という状況を生み出しがちになります。
そのような状況を避けるためにも、「サービスを提供するレイヤー(層)」をはっきりとさせ、アプリケーションの見通しをよくしておくことは大切なことです。
本節では、マジックサーブレットに機能分割のリファクタリングを施していくことで、Webアプリケーションに「サービスを提供するレイヤ」を持たせる方法を解説します。
具体的には「ファサード」というパターンを使います。ファサードは複雑な処理の呼び出しを単純化するためのパターンです。
マジックサーブレットの長くて複雑な業務処理は別のクラスに移動させて、サーブレットからは「業務処理を呼び出すだけ」という構成にしていきます。
今回ご紹介する「サービスを提供するレイヤ」を作るためのファサードパターンは、J2EEパターンではセッションファサードパターンと呼ばれています。 セッションファサードはEJBのセッションBeanを対象としたパターンです。
本特集ではEJBを対象としないため、セッションファサードという名前は使用せず、単にファサードと呼んでいますが、パターンの目的・手法はほとんど同じです。
パターン解説
ファサードは「建物の正面」という意味です。
筆者はファサードのことを考えるときは大きな建物の「シンプルな正面玄関」をイメージしています。 「シンプルな正面玄関」の奥には複雑な処理が隠されていますが、呼び出す側は「シンプルな正面玄関=シンプルなメソッド」だけを意識すればよいわけです(図5)。
まずは簡単な例を見てみましょう。 図6の左側のdoGet()メソッドは「パラメータの取得」「データベース処理」「業務処理」などが渾然一体となって、かなり複雑でマジックなサーブレットと化しています。 図6の右側のように複雑な業務処理の部分を「ファサード」クラスに移動・分割することで、サーブレットからは「ファサードを呼び出すだけ」の状態にすることができます。
具体的な例
より具体的な例を見てみましょう。
サンプルは、シンプルな書籍データの更新・挿入処理を行うものです。 今回はサーブレットでなくStrutsのアクションクラスを題材にしています。 Strutsのアクションクラスも役割としてはサーブレットとまったく同じですので、複雑な「マジックアクション」は極力避けるべきです。
ここでは、「マジックアクションの状態」から、ファサードにより「アプリケーションの提供する機能が分割された状態」にリファクタリングを施していきます。
MEMO |
Strutsアクションクラスの役割 |
---|
Strutsのアクションクラスの役割はWebコントローラと呼ばれます。 WebコントローラはWeb特有の処理を行い、業務処理との橋渡しを行うのが目的です。 つまりリスト4-⑤「次画面への遷移」やリスト5-①「フォームBeanからドメインオブジェクトにデータをコピー」がアクションクラスにあることはまったく問題ありません。 |
◎第1段階:マジックアクション
まずは全ての処理がアクションクラスに実装されたマジックアクションを見てみましょう(リスト4)。
リスト4 UpdateBookAction1.java
/**
* 書籍データ更新・挿入アクション(マジックアクション)です。
*/
public class UpdateBookAction1 extends Action {
/** ログです。 */
private static Log log = LogFactory.getLog(UpdateBookAction1.class);
/**
* 書籍の更新処理を行います。
*/
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest req, HttpServletResponse res)
throws Exception {
UpdaateBookForm bookForm = (UpdateBookForm) form;
Connection conn = null;
PreparedStatement st = null;
PreparedStatement st2 = null;
try {
// ①コネクションの取得
Class.forname("org.hsqldb.jdbcDriver");
conn = DriverManager.getConnection(
"sa", "", "jdbc:hsqldb:hsql://localhost");
// ②更新処理
st = conn.prepareStatement(
"update tdl_book set title=?, price=? where id=?");
st.setString(1, bookForm.getTitle());
st.setInt(2, bookForm.getPrice());
st.setInt(3, bookForm.getId());
int result = st.exexuteUpdate();
// ③更新件数が0なら挿入処理
if (result == 0) {
// 挿入処理
st2 = conn.prepareStatement(
"insert into tdl_book(id, title, price) "
+ "values(?, ?, ?)");
st2.setInt(1, bookForm.getId());
st2.setString(2, bookForm.getTitle());
st2.setInt(3, bookForm.getPrice());
int result2 = st.exexuteUpdate();
}
conn.commit();
} catch (SQLException e) {
log.error(e.getMessage(), e);
throw new SQLRuntimeException(e);
} finally {
if (st != null) { st.close(); }
if (st2 != null) { st2.close(); }
if (conn != null) { conn.close(); } …④
}
return mapping.findForward("success"); …⑤
}
}
execute()メソッドでは以下の処理が行われています。
このうち①~④までが更新・挿入処理に当たりますが、アクションクラスに直接処理が埋め込まれているため、他 のクラスからこの機能を呼び出すことはできません。
そこで、データベースにアクセスするクラスは後述のDAO(データアクセスオブジェクト)に切り出してみたいと思います。
◎第2段階:データベースにアクセスするコードを別クラスに分割
データベースにアクセスするコードを別クラスに分割したものがリスト5・リスト6です。
リスト5 UpdateBookAction2.java
/**
* 書籍データ更新・挿入アクション(DAOを分割)です。
*/
public class UpdateBookAction2 extends Action {
/**
* 書籍の更新処理を行います。
*/
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest req, HttpServletResponse res)
throws Exception {
UpdateBookForm bookForm = (UpdateBookForm) form;
// ①フォームBeanからドメインオブジェクトにデータをコピー
Book book = new Book();
BeanUtils.copyProperties(book, form); …②
③ |
|
return mapping.findForward("success"); } }
リスト6 BookDao.java
/**
* 書籍データのデータアクセスオブジェクトです。
*/
public class BookDao {
/** ログです。 */
private static Log log =
LogFactory.getLog(BookDao.class);
/**コネクションです。 */
Connection conn;
/**
* コンストラクタです。
*/
public BookDao() {
conn = SqlUtil.createConnection();
}
/**
* 更新処理を行います。
* @param book書籍データ
* @return 更新件数
*/
public int update(Book book) {
// 更新処理
preparedStatement st = null;
try {
st = conn.prepareStatement(
"update tdl_book set title=?, price=? where id=?";
st.setString(1, book.getTitle());
st.setInt(2, book.getPrice());
st.setInt(3, book.getId());
return st.executeUpdate();
} catch {
log.error(e.getMessage(), e);
throw new SQLRuntimeException(e);
} finally {
sqlUtil.close(st);
}
}
/**
* 挿入処理を行います。
* @param book書籍データ
* @return 更新件数
*/
public int insert(Book book) {
// 挿入処理
preparedStatement st = null;
try {
st = conn.prepareStatement(
"insert into tdl_book(id, title, price) "
+ "values(?, ?, ?)");
st.setInt(1, book.getId());
st.setString(2, book.getTitle());
st.setInt(3, book.getPrice());
return st.executeUpdate();
} catch(SQLException e) {
log.error(e.getMessage(), e);
throw new SQLRuntimeException(e);
} finally {
sqlUtil.close(st);
}
}
/**
* Connectionをコミットします。
*/
public void commit() {
sqlUtil.commit(conn);
}
}
コネクションの取得、SQLの発行などをBookDaoクラスに移動しました。 たったこれだけで、アクションクラスはすっきり見やすくなりました。
この変更でBookDaoクラスはUpdateBookAction2以外のアクションクラスから呼び出すことができるようになりました。
その他変更点としてはリスト5-②でJakarta CommonsのJavaBeans操作用コンポーネントであるBeanUtils を使って、UpdateBookFormからBookオブジェクトにデータのコピーをおこなっています。 このことでBookDaoにStruts固有のクラスであるフォームBeanを渡さないようにしています。 「別にUpdateBookFormを直接渡してもいいんじゃないの?」という声が聞こえてきそうですが、UpdateBookFormを直接渡すとBookDaoはUpdateBookFormに極度に依存します。
この業務専用のBookオブジェクトをきちんと作ってを渡すことで、BookDaoはさまざまな場所で利用できる「汎用性の高い」クラスになります。
またコネクションの取得、クローズなどの共通的な処理はSqlUtilという「ユーティリティクラス」に移動しています。 ユーティリティクラスについては第4章をご覧ください。
◎第3段階:ファサードの適用
第2段階でもずいぶんすっきりしたような気がしますね。
ここではさらにもう一歩踏み込んでみます。リスト5-③をご覧ください。
ここには「更新件数が0件だったら、挿入処理を実行する」というルールが潜んでいます。 このルールが共通化されない以上、BookDaoクラスを呼び出すたびにここに書かれたルールを記述する必要があります。 また、BookDaoの生成やコネクションのコミットなども共通化できそうです。 さらに大きな視点で見ると、リスト5-③の部分が「書籍の更新・挿入処理」というサービスを実装していると言えます。
このサービス部分をファサードとして抽出することで、「書籍の更新・挿入処理」をシンプルなメソッド呼び出しに置き換えることができます。
処理のファサード化は簡単です。 ファサード化したいコードを、別のクラスのメソッドに移動して、必要な情報を引数として渡すだけです。
ここではまずサービスの仕様を明確化するために、サービスのインターフェースを定義しています(リスト7)。
リスト7 BookService.java
/**
* 書籍管理サービスインタフェースです。
*/
public interface BookService {
/**
* 書籍データの更新・挿入処理を行います。
* 更新処理でデータが1件も更新されなかった場合、
* 新規データとみなして挿入処理を行います。
* @param book書籍データ
*/
void updateOrInsertBook(Book book);
}
そのサービスを実装するクラスにコードを移動しています(リスト8)。
リスト8 BookServiceImpl.java
/**
* 書籍管理サービスインタフェース実装クラスです。
*/
public interface BookServiceImpl implements BookService {
public void updateOrInsertBook(Book book) {
// DAOクラスの生成
BookDao bookDao = new BookDao();
// 更新処理
int result = bookDao.update(book);
// 更新件数が0なら挿入処理
if (result == 0) {
// 挿入処理
bookDao.insert(book);
}
bookDao.insert(book);
}
}
リスト9のUpdateBookAction3では①のようにBookServiceのupdateOrInsertBook()メソッドを呼び出すだけです(図7~8)。
リスト9 UpdateBookAction3.java
/**
* 書籍データ更新・挿入アクション(サービスレイヤの導入)です。
*/
public class UpdateBookAction3 extends Action {
/**
* 書籍の更新処理を行います。
*/
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest req, HttpServletResponse res)
throws Exception {
UpdateBookForm bookForm = (UpdateBookForm) form;
// FormBeanからドメインオブジェクトにデータをコピー
Book book = new Book();
BeanUtils.copyProperties(book, form);
// ①更新 or 挿入処理
BookService service = new BookServiceImpl();
service.updateOrInsertBook(book);
return mapping.findForward("success");
}
}
この変更により「書籍の更新・挿入」サービスをどこからでも簡単に呼び出すことができます。
アプリケーションが提供する機能はBookServiceを見れば一目瞭然、明確に知ることができます。
今後、「書籍の検索」処理が追加されたら、BookServiceにメソッドを追加して、BookServiceImplに実装コードを書くというルールを保っていけば、常にアプリケーションが提供する機能をBookServiceで把握していくことができることでしょう。
まとめ
◎ファサード本来の目的
今回のサンプルでは「サービスを提供するレイヤ」を実現するためにファサードパターンを使用しましたが、 ファサードパターンの本来の目的は複雑なクラス・API呼び出しを実行するための、シンプルな入り口を用意する ことです。
◎サービスインタフェースを作ることのメリット
サービスインタフェースを作るのがめんどくさいと思われる方も多いことでしょう。 サービスインタフェースを作るメリットは次のような点があります。
- ①ルールの明確化
- ②モックオブジェクトが使える
①のルールは、「アクションクラスからの業務処理の呼び出しはサービスインタフェース経由でのみ行うこと」 というシンプルなものになります。
ルールがないと、アクションクラスから直接BookDaoを呼び出したいという誘惑には勝てず、サービスレイヤを構築することは難しいはずです。
②の「モックオブジェクト」とは、テスト用の仮の実装を持たせたオブジェクトのことです。
BookDaoやBookServiceの実装がまだない状態の時に、BookServiceの仮の実装をモックオブジェクトとして用意することで、BookDaoやBookServiceの実装を待たずにアクションクラスや画面の実装・テストができるようになります。
これは、業務処理とアクションを含めた画面周りの処理を平行して実装・テストするときに役に立つテクニックです (リスト10)。
リスト10 MockBookService.java
/**
* モック版書籍管理サービスインタフェース実装クラスです。
*/
public class MockBookService implements BookService {
public void updateOrInsertBook(Book book) {
// 何も実装せずに実装したように見せかける
}
}
◎こんなときにも使える
ファサードは複雑なAPIを定期的なコードで呼び出す必要がある場面で有効です。
たとえばJavaMailを使ったメール送信は、割と複雑なAPIを使って定期的なコードを書く必要があり面倒です。 このように面倒だと感じる部分はファサード化が適用できないか検討してみましょう(リスト11)。
リスト11 MailFacade.java
public class MailFacade {
// メール送信の窓口
public void send(
String subject, String to, String from, String message) {
// メール送信コードが続く
}
}
◎ユーティリティークラスとどう違うの?
乱暴に言ってしまえば、ファサードとユーティリティークラスはコードを共通化している点で似ています。 ただし、ユーティリティークラスはさまざまな場所で利用する共通的なコードをまとめたものです。
ファサードはもう少し大きな範囲で、複雑なAPIを適切な順番で実行するための、シンプルな入り口を作るという目的があります。
役員退職金の効果的な準備方法
毎月7万積立であり?
小規模企業共済で退職金を準備する5つのメリットと3つのデメリット