私はほとんどコードを再利用することが有利である理由を議論する必要はありません。コードの再利用は、通常より高速なアプリケーション開発を行い、バグの削減になります。コードをパッケージ化して再利用されると、コードの唯一の小片は、プログラムの正確性を確保するためにチェックすることができます。あなたが唯一のアプリケーション全体で一つの場所でデータベース接続を開閉する必要がある場合、そのはるかに容易に確保するために適切に接続されています。しかし、私はあなたが既に知っているものを確信しています。
再利用可能なコードの2種類があり、私はタイプを再利用するためにそれらを呼び出します。
- 機能の再利用(リユースアクション)
- 再利用のコンテキスト(文脈の再利用)
第一のタイプは、再使用タイプの最も一般的な形態であり、再使用の関数です。また、これは、ほとんどの開発者で担当者です。すなわち、特定の操作を実行する後続の命令の再使用セット。
コンテキストの第二のタイプは、すなわち、異なる機能またはコードの再利用のコンテキストと同じパッケージ内のオペレーションコードとの間の同一のコンテキストを再利用される(コンテキストは、本明細書で同じ操作命令の系列を指します)。けれどもそれは制御の反転でますます人気がありますが、それは一般的ではありません。また、コンテキストの再利用とは、明示的に記載されていないので、システムの使用と同様の機能を再利用しません。私はあなたがこの記事を読んだ後それが変更されます願っています。
再利用機能
関数の再利用は再利用の最も一般的なタイプです。再利用する操作を実行するための命令のセットです。次の2つの方法がデータベースからデータを読み取るために、次のとおりです。
public List readAllUsers(){
Connection connection = null;
String sql = "select * from users";
List users = new ArrayList();
try{
connection = openConnection();
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet result = statement.executeQuery();
while(result.next()){
// 重用代码
User user = new User();
user.setName (result.getString("name"));
user.setEmail(result.getString("email"));
users.add(user);
// END 重用代码
}
result.close();
statement.close();
return users;
}
catch(SQLException e){
//ignore for now
}
finally {
//ignore for now
}
}
public List readUsersOfStatus(String status){
Connection connection = null;
String sql = "select * from users where status = ?";
List users = new ArrayList();
try{
connection = openConnection();
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, status);
ResultSet result = statement.executeQuery();
while(result.next()){
// 重用代码
User user = new User();
user.setName (result.getString("name"));
user.setEmail(result.getString("email"));
users.add(user);
// END 重用代码
}
result.close();
statement.close();
return users;
}
catch(SQLException e){
//ignore for now
}
finally {
//ignore for now
}
}
経験豊富な開発者のために、すぐに再利用可能なコードを見つけることができるかもしれません。上記のコメントコード「再利用コードは」同じ場所なので、パッケージを再利用することができます。これらのレコードは、ユーザ操作のユーザーのインスタンスに読み込まれます。これらの行は、例えば、自分のやり方にカプセル化することができます。
// 将相同操作封装到 readUser 方法中
private User readUser(ResultSet result) throws SQLException {
User user = new User();
user.setName (result.getString("name"));
user.setEmail(result.getString("email"));
users.add(user);
return user;
}
今、呼上述の二つの方法readUser()
のプロセス(以下の例では、最初の方法を示しています)。
public List readAllUsers(){
Connection connection = null;
String sql = "select * from users";
List users = new ArrayList();
try{
connection = openConnection();
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet result = statement.executeQuery();
while(result.next()){
users.add(readUser(result))
}
result.close();
statement.close();
return users;
}
catch(SQLException e){
//ignore for now
}
finally {
//ignore for now
}
}
readUser()
この方法はまた、独自のクラスで修飾子を使用することができますprivate
隠されました。
これらは、機能の再利用についての詳細です。関数は、その再利用の目的を達成するための方法で特定の操作または包装の種類を実行するための命令のセットを再利用することです。
パラメータ化
時には、あなたは、アクションのセットを再利用したいのですが、これらのアクションはどこでも使用とまったく同じではありません。例えば、readAllUsers()
及びreadUsersOfStatus()
方法は、接続を開くステートメントを準備し、それを実行し、結果セットを反復することです。唯一の違いは、readUsersOfStatus()
必要PreparedStatement
にパラメータを設定します。我々は、すべての中にカプセル化された動作可能readUserList()
方法。次のように:
private List readUserList(String sql, String[] parameters){
Connection connection = null;
List users = new ArrayList();
try{
connection = openConnection();
PreparedStatement statement = connection.prepareStatement(sql);
for (int i=0; i < parameters.length; i++){
statement.setString(i, parameters[i]);
}
ResultSet result = statement.executeQuery();
while(result.next()){
users.add(readUser(result))
}
result.close();
statement.close();
return users;
}
catch(SQLException e){
//ignore for now
}
finally {
//ignore for now
}
}
今、私たちはからのものreadAllUsers()
とreadUsersOfStatus()
呼び出すreadUserList(...)
メソッドを、異なる動作パラメータを与えられました:
public List readAllUsers(){
return readUserList("select * from users", new String[]{});
}
public List readUsersWithStatus(String status){
return readUserList("select * from users", new String[]{status});
}
私はあなたが能力を再利用するために、他のより良い方法を見つけることができると確信している、とパラメトリックを使用するようにそれらが容易になります。
再利用のコンテキスト
コンテキストの再利用および再利用機能は多少異なります。再利用のコンテキストの再利用は、各種の操作が常にこれらの命令の間に実行され、一連の命令です。言い換えれば、前と後の異なる行動のさまざまな方法を使って文を繰り返しました。したがって、通常、コンテキストクラス反転制御スタイルを再利用につながります。再利用、再利用のコンテキスト例外処理、接続性、およびライフサイクル管理で、フロー反復は閉鎖され、非常に効果的な方法の事務の多くの他の一般的な操作のコンテキスト。
InputStreamを使用している2つの方法があります、次のとおりです。
public void printStream(InputStream inputStream) throws IOException {
if(inputStream == null) return;
IOException exception = null;
try{
int character = inputStream.read();
while(character != -1){
System.out.print((char) character); // 不同
character = inputStream.read();
}
}
finally {
try{
inputStream.close();
}
catch (IOException e){
if(exception == null) throw e;
}
}
}
public String readStream(InputStream inputStream) throws IOException {
StringBuffer buffer = new StringBuffer(); // 不同
if(inputStream == null) return;
IOException exception = null;
try{
int character = inputStream.read();
while(character != -1){
buffer.append((char) character); // 不同
character = inputStream.read();
}
return buffer.toString(); // 不同
}
finally {
try{
inputStream.close();
}
catch (IOException e){
if(exception == null) throw e;
}
}
}
動作フローの両方の方法が異なっています。しかしながら、これらの操作を取り巻くコンテクストは同じです。反復コンテキストコードとInputStreamを閉じます。上記のコードに加えて、ユーザコメント異なるコンテキストコードです。
上記に示したように、コンテキストは、例外処理に関連し、そして反復後にオフ適切な流れを確実にするために。何度も何度も、このようなエラー処理とリソース解放コードを書くことは非常に退屈でエラーを起こしやすいです。エラー処理と適切な接続プロセスは、JDBCトランザクションではより複雑です。一度コードを記述し、それを明確に、それはどこでも容易になることを再利用します。
幸いなことに、パッケージはシンプルな文脈です。クラスコンテキスト、公共コンテキストを作成し、それを置きます。抽象操作インターフェースに様々な動作命令、および、インターフェイスの動作の実装クラスにカプセル化されたそれぞれの動作の文脈で(型操作と呼ぶ)、ちょうどクラスの動作の一例を挿入コンテキストへ。例としては、コンストラクタコンテキストオブジェクトの引数としてクラスの操作によって達成さ、又は一例として、クラスの動作をコンテキストを行う具体的な方法にパラメータとして渡すことができます。
次の例は、上記のコンテキストとオペレータインタフェースのパーティションを示します。StreamProcessor
パラメータとして(オペレータインタフェース)方法。StreamProcessorContext
processStream()
// 流处理插件接口
public interface StreamProcessor {
public void process(int input);
}
// 流处理上下文类
public class StreamProcessorContext{
// 将 StreamProcessor 操作接口实例化并作为参数
public void processStream(InputStream inputStream, StreamProcessor processor) throws IOException {
if(inputStream == null) return;
IOException exception = null;
try{
int character = inputStream.read();
while(character != -1){
processor.process(character);
character = inputStream.read();
}
}
finally {
try{
inputStream.close();
}
catch (IOException e){
if(exception == null) throw e;
throw exception;
}
}
}
}
次の例のようになりましとして使用することができますStreamProcessorContext
ストリーミングコンテンツをプリントアウトの種類:
FileInputStream inputStream = new FileInputStream("myFile");
// 通过实现 StreamProcessor 接口的匿名子类传递操作实例
new StreamProcessorContext()
.processStream(inputStream, new StreamProcessor(){
public void process(int input){
System.out.print((char) input);
}
});
入力ストリームなどを読むと、シーケンスの次の文字の内容を追加しました:
public class StreamToStringReader implements StreamProcessor{
private StringBuffer buffer = new StringBuffer();
public StringBuffer getBuffer(){
return this.buffer;
}
public void process(int input){
this.buffer.append((char) input);
}
}
FileInputStream inputStream = new FileInputStream("myFile");
StreamToStringReader reader = new StreamToStringReader();
new StreamProcessorContext().processStream(inputStream, reader);
// do something with input from stream.
reader.getBuffer();
あなたが見ることができるように、異なる挿入することにより、StreamProcessor
対流インターフェイスに何もします。いったんStreamProcessorContext
完全に実現ストリームが閉じられていないについては、あなたが気にすることはありません。
コンテキストの再利用は、使用中のストリーム処理の外に、他の多くの環境ですることができ、非常に強力です。明確かつ正確な実施形態にトランザクションデータベース接続ハンドル(オープン - プロセス - コミットを()/ロールバック() - クローズ())。( - アクセス共有リソース - アンロック()ロック())別のユースケースは、NIO臨界ゾーンおよびチャネル処理スレッドの同期です。これは、未チェック例外のAPIに例外を確認することも可能です。
あなたがあなた自身のプロジェクトにコードを再利用するために適切なコンテキストを見つけたら、次の操作モードを探します。
- 通常動作の前に(前の一般的なアクション)
- 特別な操作(特殊アクション)
- 通常動作(後に一般的な作用)の後
通常の操作は、コンテキストの再利用を実現することが前と後のあなたは、このようなパターンを見つけた場合。
テンプレートの方法としてコンテキスト
時には、あなたはコンテキスト内でより多くのプラグ・ポイントを持っていることになるでしょう。少ないステップ数の状況、そしてあなたは、各ステップのコンテキストをカスタマイズすることができますしたい場合は、テンプレートコンテキストメソッドとして実装することができます。テンプレートメソッドデザインパターンは、GOFです。基本的に、一連のステップにテンプレートアルゴリズムまたはプロトコルに記載の方法。テンプレート一般に、単一の基底クラスの実装の方法、およびアルゴリズムまたはプロトコルの各ステップのための方法を提供します。ステップのいずれかをカスタマイズするには、単に基本クラスを拡張し、自己定義に方法ステップをオーバーライドするクラステンプレートを作成します。
次の例では、テンプレートJdbcContextとして実装される方法です。サブクラスは、接続口をオーバーライドして、カスタム動作を提供するために、閉じることができます。我々は常にオーバーライドする必要がありprocessRecord(ResultSet result)
、それは抽象的であるため、この方法を。この方法は、使用して動作していないコンテキストを提供しJdbcContext
、異なる状況下での動作を異なります。この例は完璧ではありませんJdbcContext
。唯一のコンテキストを実装する場合、テンプレートメソッドを使用する方法を実証するために使用されます。
public abstract class JdbcContext {
DataSource dataSource = null;
// 无参数的构造函数可以用于子类不需要 DataSource 来获取连接
public JdbcContext() {
}
public JdbcContext(DataSource dataSource){
this.dataSource = dataSource;
}
protected Connection openConnection() throws SQLException{
return dataSource.getConnection();
}
protected void closeConnection(Connection connection) throws SQLException{
connection.close();
}
// 必须始终重写 processRecord(ResultSet result) 方法
protected abstract processRecord(ResultSet result) throws SQLException ;
public void execute(String sql, Object[] parameters) throws SQLException {
Connection connection = null;
PreparedStatement statement = null;
ResultSet result = null;
try{
connection = openConnection();
statement = connection.prepareStatement(sql);
for (int i=0; i < parameters.length; i++){
statement.setObject(i, parameters[i]);
}
result = statement.executeQuery();
while(result.next()){
processRecord(result);
}
}
finally {
if(result != null){
try{
result.close();
}
catch(SQLException e) {
/* ignore */
}
}
if(statement != null){
try{
statement.close();
}
catch(SQLException e) {
/* ignore */
}
}
if(connection != null){
closeConnection(connection);
}
}
}
}
これは、サブカテゴリーのリストを読み込むための拡張JdbcContextユーザーであります:
public class ReadUsers extends JdbcContext{
List users = new ArrayList();
public ReadUsers(DataSource dataSource){
super(dataSource);
}
public List getUsers() {
return this.users;
}
protected void processRecord(ResultSet result){
User user = new User();
user.setName (result.getString("name"));
user.setEmail(result.getString("email"));
users.add(user);
}
}
ここでReadUsersカテゴリを使用する方法は次のとおりです。
ReadUsers readUsers = new ReadUsers(dataSource);
readUsers.execute("select * from users", new Object[0]);
List users = readUsers.getUsers();
場合ReadUsers
クラスが接続を取得する必要があり、接続プールのプールからバック接続を解放するためにそれらを使用した後、あなたは無効にすることができますopenConnection()
し、closeConnection(Connection connection)
接続方法に挿入されます。
プロセスによって挿入操作のコードを書き換える方法に注目してください。JdbcContext
サブクラスがオーバーライドprocessRecord
特定の記録方法を提供する方法。StreamContext例では、オペレーションコードが別のオブジェクトにカプセル化され、そしてメソッドパラメータを提供します。実装ユーザインターフェイスStreamProcessor
オブジェクトは、パラメータとして渡されたStreamContext
クラスのprocessStream(...)
メソッド。
コンテキストの実装では、あなたは両方の技術を使用することができます。JdbcContext
クラスインターフェース動作が実現されてもよいConnectionOpener
とConnectionCloser
の引数としてオブジェクトexecute
メソッド、またはコンストラクタへの引数として。私にとっては個人的に、私は2つの理由から、別の操作オブジェクトとユーザーインターフェースを使用することを好みます。まず、オペレータコードを可能にするより簡単にテストユニットを分離することができ、第二に、それは、複数のコンテキストで操作コードの再利用を行います。もちろん、オペレーションコードは、コードにおける複数の位置で使用することができるが、これは利点です。結局のところ、ここで私たちは、コンテキストを再利用しようとするのではなく、操作を再利用しています。
結論
今、あなたは、コードを再利用するには、2つの異なる方法を見てきましたことを。クラシック機能の再利用とあまり一般的コンテキストの再利用。コンテキストは、関数の再利用などのような一般的な再利用したいと思うでしょう。コンテキストの再利用は非常に有用な方法であり、コードは、APIの基礎となる詳細(例えばJDBC、IO NIOのAPI、等)から抽出することができます。APIは、リソースが(など、オープンとクローズ、アクセスおよびリターン)を管理する必要が含まれている場合は特に。
永続/ ORMのAPI、自動接続およびトランザクションのライフサイクル管理のために使用されるコンテキストを使用してMr.Persister重量。だから、ユーザーが開いたり、接続を閉じる、またはトランザクションをコミットまたはロールバックする権利を心配する必要はありません。Mr.Persisterは、ユーザーが自分の操作のコンテキストを挿入することができます提供します。コンテキストは、開口部、クロージング、コミットやロールバックを担当しています。
人気の春Frameworkは、コンテキストの再利用が多く含まれています。例スプリングスJDBC抽象化のために。春の開発者は、コンテキストとして再利用する「制御の反転を。」これは、春のフレームワークで使用されていないコントロールの唯一の反転型です。春のコア機能依存注射または豆の植物である「アプリケーションコンテキスト。」依存性の注入は、別の制御反転です。