Dependency Injection
「DI パターン」と これを利用した「DI コンテナ」ってのがある
「DI コンテナ」使いたいけど「DI パターン」って何?状態 > 荒いけどまとめる
ここにあることが全部ではないので、正確なことは最下記リンクへ
DI パターンとは何か
「インスタンス変数にオブジェクトを割当てること」
「或るクラスに依存するオブジェクトの作成を、他の誰かの責務にすること」
James Shore 曰く
「大げさな言い回しだ(良い意味でと付け加えておく)」
まず DI パターンが出来るまでの実情
概要
- 一般的に或るクラスは 多数の他クラスオブジェクト (以下 "dependencies")
を属性として持っている - "dependencies" は要求/仕様変更/テスト実行時に
メンテナンス、テストによってはスタブ/モックの用意が必要- ...この「メンテナンス、スタブ/モック用意」
やり易い方法がないものか?
- ...この「メンテナンス、スタブ/モック用意」
詳細(例を交え)
- 下サンプルの SmileCar について、 Wheel/Engine の "dependencies" は vendorABC である
- もし、"dependencies" が vendorABC ではなく vendorXYZ になった場合
- "dependencies" をそれぞれ vendorABCWheel(), vendorABCEngine() を変更する
- ... が、これって本当に良いやり方?
- メンテは面倒(影響範囲調査など、巨大クラスになれば尚更)
- 責任範囲という面でも SmileCar(車自身) が
"dependencies"(Wheel(タイヤ) や Engine(エンジン)) の「生成」にまで
責務を持つべき?
- ... が、これって本当に良いやり方?
(before)
(Whee/Engine がそれぞれ 抽象クラスを具象化したクラスであったり
interface を implements している前提をかなりすっ飛ばしているのは意図的)
public class SmileCar { private DatabaseUtil db; private Wheel wheel = new vendorABCWheel(); // 仕様変更でメンテが必要になりそう private Engine engine = new vendorABCEngine(); // 同上 public Car() {} public showWheelNameAndEngineName() { try { // テストの時はどこの DB 見にいく? // getConnection() 内で 本番の時は... テストの時は... // って 分岐してないですよね? db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
(after)
public class SmileCar { private DatabaseUtil db; private Wheel wheel = new vendorXYZheel(); // vendorABC > vendorXYZ private Engine engine = new vendorXYZEngine(); // 同上 public Car() { db = new DatabaseBase(); } public showWheelNameAndEngineName() { try { // テストの時はどこの DB 見にいく? // getConnection() 内で 本番の時は... テストの時は... // って 分岐してないですよね? db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
DI パターンの登場
やることは単純に、冒頭通り
「インスタンス変数にオブジェクトを割当てること」
上の SmileCar (仕様変更後) を例にする
インスタンス変数にオブジェクトを割当てる
まずは Wheel/Engine オブジェクトをインスタンス変数に割当てる
これで、Wheel/Engine の仕様変更があっても「SmileCar クラス自身」は手をつけなくていい
public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(Wheel wheel, Engine engine) { db = new DatabaseBase(); this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
次に db オブジェクトをインスタンス変数に割当てる
クラス設計がいけてないので「え?」となるが、あくまで一例
public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(DatabaseUtil db, Wheel wheel, Engine engine) { this.db = db; this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
DI 自体は終わったが、データベースモック化はどんな風に?
概略コードは以下
- SmileCar は何もしなくて良い
- 本番DB に繋ぐかモックしたものを作るかは MockDatabase ないの処理で決める
public MockDatabase extends DatabaseUtil { // モック用のフィールド、メソッド用意 } public class SmilCarTest { public void TestShowWheelNameAndEngineName() { MockDatabase mockDatabase = new MockDatabase() SmileCar smileCar = new SmileCar( mockDatabase, new vendorXYZWheel(), new venXYZEngine() ); smileCar.showWheelNameAndEngineName(); smileCar.sometest(); } } public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(DatabaseUtil db, Wheel wheel, Engine engine) { this.db = db; this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { // (特に指定がなければ) 本番用の DB を使う // テスト用の DB(モック) を使う > SmileCarTest#TestShowWheelNameAndEngineName db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
「依存性の注入」について思うこと(感想レベル)
DI[Dependency Injection] が 「依存性の注入」と訳されている
少しでも英語リソースやそこにある議論を眺めたら "dependency"
が「依存」そのものじゃなくて
「オブジェクト」やこれに近しい「モノそれ自身」だと気づくはず
引用参考リンク
http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
http://stackoverflow.com/questions/130794/what-is-dependency-injection?rq=1
転載(他):Why Use Stored Procedure
- Pro
- added layer of security that can be placed on the database from
- the data functionality making it easier to manage, document, and maintain
- improved performance
- multiple client applications to have consistent database routines
- Con
- to many calls to sql-server
- hard to test
One of the most beneficial reasons to use stored procedures is the added layer of security that can be placed on the database from the calling application. If the user account created for the application or web site is configured with EXECUTE permissions only then the underlying tables cannot be accessed directly by the user account. This helps prevent hacking directly into the database tables. The risk of a hacker using the user account to run a stored procedure that has been written by you is far safer than having the user account have full insert, update and delete authority on the tables directly. Another advantage to using stored procedures, especially in medium to large scale web sites or applications, is the data functionality is separated from the application making it easier to manage, document, and maintain. For example, if an application updates the customer table in ten different places, there can be a single stored procedure and a standard procedure call from the application for this functionality. If a change needs to be made to the way a customer record is managed, then the SQL statements only need to be changed in one place, in the database layer. In most cases, the application is not affected unless the procedure call requires modification. Changing the procedure call is also easier, because a standard call is already in place. Managing the data in the data layer avoids having to keep track of embedded SQL calls that may be different in each place, whenever a change is required. Stored procedures provide improved performance because fewer calls need to be sent to the database. For example, if a stored procedure has four SQL statements in the code, then there only needs to be a single call to the database instead of four calls for each individual SQL statement. Of course there is always a tradeoff. There is an increased workload on the server side that needs to be taken into account. Another advantage to using stored procedures allows for multiple client applications written in any language and running on any platform to have consistent database routines. Each application uses the same procedures and simply has to embed a standard procedure call for the language in the calling program.
自己結合はいつ使うか
自テーブル各レコードの特性を
単純な射影や選択では比較できない場合に使う
ここでは CASE, 相関サブクエリ, 内部/外部結合などに言及しないとする
(Web 記事や書籍を眺めてみたのだが、なかなか扱いがよくわからん構文
勉強しきれてないが、これ使うのであれば
OLAP 関数使ったりテーブル設計見直したほうが、良いのでは)
例
テーブル論理要件
- 会社員テーブル(employees) が以下の属性を持っている
- 会社員ID(employeeid)
- 上司ID(bossid)
- 会社員名
- 会社員テーブルは以下の特性を持つ
- その会社員が誰を上司とするかを判断する為に
- 上司ID が 会社員ID を参照
- ただし、上司を持たない会社員は bossid が 0 である
- bossid = 0 のデータは内部で事前に用意していおく
- その会社員が誰を上司とするかを判断する為に
テーブル物理要件
CREATE TABLE employees ( employeeid INTEGER NOT NULL bossid INTEGER NOT NULL DEFAULT 0, /* 0 means "I don't have boss" */ name VARCHAR(64) NOT NULL, PRIMARY KEY (employeeid), FOREIGN KEY bossid REFERENCES(employees), CONSTRAINT employees_identifier_validation CHECK((employeeid >= 0) AND (bossid >= 0)) );
SELECT * FROM employees;
employeeid | bossid | name ------------+--------+--------------------- 0 | 0 | you have not a boss 1 | 0 | Sakano 2 | 1 | Yoneda 3 | 1 | Umino 4 | 3 | Saejima 5 | 4 | Kobayashi
要望
各社員の名前, その社員の上司名
を表示する SQL を書いて欲しい
内部でダミーの社員ってのを用意しているらしいけど
そのデータは表示しないように
実行 SQL
SELECT EMP_MAIN.name AS "When employee is...", EMP_SUB.name AS "Then Boss is..." FROM employees AS EMP_MAIN, employees AS EMP_SUB WHERE EMP_MAIN.employeeid <> 0 AND EMP_MAIN.bossid = EMP_SUB.employeeid;
When employee is... | Then Boss is... ---------------------+--------------------- Sakano | you have not a boss Yoneda | Sakano Umino | Sakano Saejima | Umino Kobayashi | Saejima
蛇足
SELECT EMP_MAIN.bossid, EMP_MAIN.name AS "When employee is...", EMP_SUB.employeeid, EMP_SUB.name AS "Then Boss is..." FROM employees AS EMP_MAIN, employees AS EMP_SUB
EMP_MAIN.bossid | When employee is... | EMP_SUB.employeeid | Then Boss is... --------+---------------------+------------+--------------------- 0 | you have not a boss | 0 | you have not a boss 0 | you have not a boss | 1 | Sakano 0 | you have not a boss | 2 | Yoneda 0 | you have not a boss | 3 | Umino 0 | you have not a boss | 4 | Saejima 0 | you have not a boss | 5 | Kobayashi 0 | Sakano | 0 | you have not a boss 0 | Sakano | 1 | Sakano 0 | Sakano | 2 | Yoneda 0 | Sakano | 3 | Umino 0 | Sakano | 4 | Saejima 0 | Sakano | 5 | Kobayashi 1 | Yoneda | 0 | you have not a boss 1 | Yoneda | 1 | Sakano 1 | Yoneda | 2 | Yoneda 1 | Yoneda | 3 | Umino 1 | Yoneda | 4 | Saejima 1 | Yoneda | 5 | Kobayashi 1 | Umino | 0 | you have not a boss 1 | Umino | 1 | Sakano 1 | Umino | 2 | Yoneda 1 | Umino | 3 | Umino 1 | Umino | 4 | Saejima 1 | Umino | 5 | Kobayashi 3 | Saejima | 0 | you have not a boss 3 | Saejima | 1 | Sakano 3 | Saejima | 2 | Yoneda 3 | Saejima | 3 | Umino 3 | Saejima | 4 | Saejima 3 | Saejima | 5 | Kobayashi 4 | Kobayashi | 0 | you have not a boss 4 | Kobayashi | 1 | Sakano 4 | Kobayashi | 2 | Yoneda 4 | Kobayashi | 3 | Umino 4 | Kobayashi | 4 | Saejima 4 | Kobayashi | 5 | Kobayashi (36 rows)
WHERE EMP_MAIN.employeeid <> 0 AND EMP_MAIN.bossid = EMP_SUB.employeeid;
When employee is... | Then Boss is... ---------------------+--------------------- Sakano | you have not a boss Yoneda | Sakano Umino | Sakano Saejima | Umino Kobayashi | Saejima
相関サブクエリはいつ(一番)使われるか
(一番)使われるのは「レコードの存在チェック」
Web 記事を色々眺めて、私なりの答え
(業務システムの SQL を見ていると「このオペレーションは、いつ、何のために使うのか」
を見失うので一回整理したい)
例
- 利用者が存在し、各属性を持つ
- 利用者 ID
- 年齢(0 以上 100 以下)
- 名前(255 文字以内の可変長)
CREATE TABLE users ( userid INTEGER NOT NULL, age INTEGER NOT NULL CHECK (age BETWEEN 0 AND 100), name VARCHAR(255) NOT NULL, PRIMARY KEY(userid) );
userid | age | name --------+-----+----------- 1 | 26 | Akayama 2 | 30 | Urano 3 | 25 | Kitano 4 | 30 | Mashita 5 | 33 | Okajima 6 | 26 | Iseno 7 | 22 | Kashima 8 | 28 | Takahashi
集計関数を使わない場合
同年齢のユーザー一覧を表示する
SELECT * FROM users AS USR WHERE EXISTS ( SELECT 1 FROM users AS USRSUB WHERE USR.userid <> USRSUB.userid AND USR.age = USRSUB.age );
userid | age | name --------+-----+--------- 6 | 26 | Iseno 1 | 26 | Akayama 4 | 30 | Mashita 2 | 30 | Urano
集計関数を使う場合
総ユーザーにおける平均年齢以上の
ユーザー一覧を表示する
SELECT AVG(age) FROM users;
avg ---- 27.5
SELECT * FROM users AS USR WHERE age >= ( SELECT AVG(USRSUB.age) FROM users AS USRSUB );
userid | age | name --------+-----+----------- 2 | 30 | Urano 4 | 30 | Mashita 5 | 33 | Okajima 8 | 28 | Takahashi
色々眺めた記事のうち一つ
RDBMS の view はいつ使われるか
StackOverflow より引用及び翻訳(意訳 加筆)
より正確な内容は本記事下段のリンク先参照
view はいくつか良いものを提供してくれる 1. View は複雑さを隠すことができる 何個かテーブルを結合したり、複雑な計算したり、 複雑ロジックを組んだクエリが必要な場合 こいつらは view に組込むことができる また、view からはテーブルと同じように select で属性を取得することができる 2. View はセキュリティメカニズムに使われうる view は特定の行 必要ならば列を制約し 項目をテーブルから取得できる その view にはパーミッションをつけることも可能だ つまりこれは、ユーザーからの必要なデータだけを見える化した 「権限」を設けることだ 3. View はレガシーコードの簡潔化に使われうる たくさんの「ぶっ飛んだテーブル」をリファクタしたい時 これらテーブルと同じ名を持った view へ置き換えが可能だ view のスキーマは(ユーザーから見れば) 置き換わるテーブルスキーマと同じままで リファクタされる実際のテーブルスキーマは変わることになる ( つまり「ユーザーから見れば何も変わってない」 「リファクタする側からすれば色々手を加えられる」) これで、「ぶっ飛んだテーブル」は自分の思いのままにリファクタができる 以上が view の有用な使い方の「数あるうち」だ
A view provides several benefits. 1. Views can hide complexity If you have a query that requires joining several tables, or has complex logic or calculations, you can code all that logic into a view, then select from the view just like you would a table. 2. Views can be used as a security mechanism A view can select certain columns and/or rows from a table, and permissions set on the view instead of the underlying tables. This allows surfacing only the data that a user needs to see. 3. Views can simplify supporting legacy code If you need to refactor a table that would break a lot of code, you can replace the table with a view of the same name. The view provides the exact same schema as the original table, while the actual schema has changed. This keeps the legacy code that references the table from breaking, allowing you to change the legacy code at your leisure. These are just some of the many examples of how views can be useful.