オブジェクト指向のこころ 読んだ まとめ
オブジェクト指向のこころを読み終えました
オブジェクト指向のこころ (SOFTWARE PATTERNS SERIES)
- 作者: アラン・シャロウェイ,ジェームズ・R・トロット,村上雅章
- 出版社/メーカー: 丸善出版
- 発売日: 2014/03/11
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る
最近は最適な設計、コードの質を上げることを特に意識して開発を行っています。
まだまだ至らずなところが多いなか、以前と比べると少しづつ質は上がってきてはいるのかなと感じています。
今回は、「デザインパターンを用いたオブジェクト指向開発」について深く学びたかったため本書を手に取りました。
デザインパターンがどういうものなのかがわかった。オブジェクト指向についてもある程度理解はしている。 でも、具体的にそれらをどう組み合わせたら柔軟で堅牢なシステムを作れるのかはわからない。
そういった人にこの本は向いているかもしれません。
この本はデザインパターンとオブジェクト指向の中間点を説明しています。
読み終えて、オブジェクト指向開発でよく言われている「インターフェースに対して実装を行う」ということの意味がよくわかりました。
ソフトウェア開発とは全く関係なさそうな、建築学の話が途中入ってきます。
建築学的視点を元にデザインパターンの本質を解き明かしていくのですが、これが非常にわかりやすかったです。
良い設計になるか、悪い設計になるかは視点の置き方によって変わると言われていますが。
その視点の置き方について本書では、「第8章 視野を広げる」で20ページにわたって深く解説しています。
その中でも特に重点的に語られているのが カプセル化です。
従来の考え方でいくとカプセル化とは「データの隠蔽」として考えられており、自分もそのように考えていました。 しかしここではそれを全面的に否定し、以下のように唱えていました。
カプセル化とは、「あらゆるものを隠蔽すること」であると考えるべきです。
考えるべき。という強い口調で語られていますが、読み終えた自分も全くその通りだと考えています。
具体的には、下記のように5つの隠蔽を実践することで堅牢で柔軟なシステムを作り上げていこうということです。
- データの隠蔽
- 実装の隠蔽
- クラスの隠蔽
- 設計の隠蔽
- 実体化の隠蔽
これはつまり、「流動的な要素を見つけ出し、それをカプセル化する」ということになります。
この考え方を特に強く反映し、またそのフォースを強く持つのがStrategyパターンとBridgeパターンです。
特にBridgeパターンについては30ページかけて説明されています。
ここを読み終えた後には、Bridgeパターンの目的である
「実装から抽象的側面を切り出して、それを独立して変更できるようにする。」
ということに意味がすごくよく理解でき、かつ解決策に導くための視点の置き方の重要性に気付かされます。
第14章では「デザインパターンの原則と戦略」についてまとめられています。
開放/閉鎖原則 (オープン/クローズド)
新たな機能を個別に、すなわちモジュール化された形で追加していけるようにすることで、
結合コストを最小化したソフトウェア設計にするということ。
依存性の逆転原則
抽象的即名は詳細に依存してはならない。詳細が抽象的側面に依存するべきである
- 流動的要素のカプセル化原則
デザインパターンを適用するための問題領域の洗い出しを行う方法に、 「共通性/可変性分析」という方法が紹介されています。
- 共通性分析
時間が経ってもあまり変化しないもの
概念上の観点
- 可変性分析
流動的要素
実装上の観点
読んで理解は深められたものの、まだまだ実践に落とし込めていることが少ないので何度も読み返しながら実践し続けていきたい。
ここに書かれている設計手法、視点の置き方、分析テクニックなどを用いて、 0から柔軟で堅牢なシステムを構築できるようになれば一人前のオブジェクト指向設計者になれると思う。
がんばりたい。
Reactでテストを行う まとめ その2
実際にテストを書いていきたい
前記事で紹介したツール。
- React
- enzyme
- Mocha
- sinon
- power-assert
- chai
- jsdom
アサーションライブラリは好みで選ぶ。今回はpower-assertを使ってみる。
前提
今回はReactのコンポーネントに対してのテストを行います。 かつ、ES6で記述し、Babelでトランスパイルしている。
- ES6
- Babel
- React
パッケージ
下記コマンドを実行し必要なパッケージをインストールします。
npm i -D mocha enzyme react-addons-test-utilds jsdom bable-register power-assert
インストールされたパッケージ
react-addons-test-utils
は直接は利用しないが、enzymeで必要とされるのでインストール
package.jsonには以下のように追加されます
ちなみにパッケージ管理にはyarnがオススメです。
{ "devDependencies": { "babel-register": "^6.22.0", "enzyme": "^2.7.1", "eslint-plugin-react": "^6.9.0", "jsdom": "^9.9.1", "mocha": "^3.2.0", "power-assert": "^1.4.2", "react-addons-test-utils": "^15.4.2", "sinon": "^1.17.7" } }
構成
├── package.json ├── src │ └── components │ └── index.js │ └── test ├── .setup.js └── components └── index.test.js
構成は参考までに。
testディレクトリ以下にテストファイルを置きます。
命名として index.test.js
のように拡張子の前にtestを追加します。
スクリプト
テストを実行するためにスクリプトを定義します。
{ "scripts": { "test": "mocha ./app/test/*.test.js -r ./app/test/.setup.js --compilers js:babel-register" } }
package.jsonに記述することにより、
npm run test
で実行することでテストを実行することができます。
-r ./app/test/.setup.js
と記述することでテストの実行前に.setup.jsをrequireし読み込んだ上でテストが実行されるようになります。
.setup.js
.setup.jsを用意します enzymeを用いてフルレンダリングのテストを行いたい場合、documentオブジェクトや、navigatorオブジェクト、windowオブジェクトにアクセスできる必要があります。 アクセスできる環境を設けるために.setup.jsではjsdomを用いて値をセットしておきます。
import { jsdom } from 'jsdom'; global.document = jsdom; global.window = document.defaultView; global.Element = () => {}; Object.keys(document.defaultView).forEach((property) => { if (typeof global[property] === 'undefined') { global[property] = document.defaultView[property]; } }); global.navigator = { userAgent: 'node.js' };
Reactでテストを行う まとめ その1
Reactでテストを行う まとめ
Reactのテスト環境を構築する際に色々と調べた結果。
もうツールとか色々ありすぎてわけがわからん。
となったので。
もし自分と同じ境遇にいる人がいたら少しでも手助けになれば良いなと思い、自分が調べたことをまとめておく。
技術スタックは以下となった。
- ES6
- Babel
- Browserify / Webpack
- React
- enzyme
- Mocha
- sinon
- power-assert
ぱっと見、開発を支えてるもので8つもある。 テストでは4つのツールを上手く組み合わせて行う。
enzyme
enzyme 呼び方: エンザイム 意味: 酵素?
2016年2月現在で、GithubStarが8000越え
Reactのドキュメントには 「Airbnbが作ったやつがあってこっちの方が良さげだよ」 的なことが書いてあって本家顔負けな感じ
Document LINK
Reactのコンポーネントテストを良い感じにしてくれる。react-addon-test-utilsなどを使わずにテストできる
- Shallow Rendering
- Full Rendering
- Static Rendering
Mocha
Mocha 呼び方: モカ
Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun.
テスト実行を担う describe/it などのテストのベースとなる機能を提供してくれる
RSpecとかに似てる気がする。 describeでテストの大枠を決め、it内でテストの具体的な内容を定義する。
describe('Test Title', () => { it('Equal', () => { const two = 3; assert(two === 3); }); }
mochaはアサーションライブラリを自由に選択できる。 chaiかpower-assertで悩んだがpower-assertを採用した 理由は、なるべくシンプルに使えて学習コストが高くないものにしたかった。 power-assertとchaiを比べてみるとchaiの方が文法が豊富いろいろな書き方でテストを書くことができ、柔軟性が高いと感じた。 power-assertはシンプルにassert()だけでいけるのと。テストが失敗した時の出力結果がわかりやすかった。
追記: どうやらpower-assertはNode.jsのAssert APIを拡張したものらしい。
power-assert
power-assert 呼び方: パワーアサート
Power Assert in JavaScript. Provides descriptive assertion messages through standard assert interface. No API is the best API.
Chai
Chai 呼び方: チャイ
アサーションライブラリ power-assertと対照的に豊富なAPIがありテストを書くことができる。
Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.
Rails Validationについて
データ管理はしっかりやりたい
DBにデータが保存される時に、そのデータが正しいかどうかを検証する仕組みをバリデーションという。
バリデーションのトリガー
バリデーションが走るメソッド群
- create
- create!
- save
- save!
- update
- update!
上記のメソッドはオプションでバリデーションをスキップすることも可能
save(validate: false)
バリデーションがスキップされるメソッド群
- decrement!
- decrement_counter
- increment!
- increment_counter
- toggle!
- touch
- update_all
- update_attribute
- update_column
- update_columns
- update_counters
Validate
通常のバリデーションです。 modelに定義して利用します。
class Recipe < ActiveRecord::Base # これでtitleがnullだとデータのInsertが行われず、弾かれます validates :title, presence: true end
valid?
とinvalid?
が用意されておりboolean型の値が返ってきます。
Recipe.new(title: "レシピタイトル").valid? #=> true Recipe.new(title: null).valid? #=> false Recipe.new(title: "レシピタイトル").invalid? #=> false Recipe.new(title: null).invalid? #=> true
カスタムValidate
単体のmodelで独自のvalidationを定義し、使いたい場合に利用する。
validate link_url_cannot_be_load def link_url_cannot_be_load if @obj.url.include?(BASIC_DOMAIN) errors[:link_url] << "でURLは利用することができません。可能ドメインか確認してください" end end
カスタムValidator
複数のmodelで独自のvalidationを定義し、使いまわしたい場合に利用する。
もしくは単体のmodelでも複数の独自validationを定義し使いたい場合に利用する。
開発者にもよるが、自分は上記の理由と定義を決めている。ここは個人の好みにもよるのかもしれない・・・
そしてRailsのapp/以下にvalidators
ディレクトリを作ってその中にカスタムvalidatorをまとめておく
カスタムバリデータはActiveModel::Validatorを拡張したクラス。
クラスにはvalidateメソッド
が実装されている必要がある。
class Recipe << ActiveRecord::Base include ActiveModel::Validations validates_with ContentValidator end class ContentValidator < ActiveModel::Validator # 必ずvalidateメソッドを実装する # 引数にはレコードを1つ取り、それに対してのバリデーションを実行する def validate(record) if record.description.blank? record.errors[:description] << "が空です" end end end
- Validates
- カスタムValidate
- カスタムValidator
3つのバリデーションを使いこない堅牢なシステムを構築していきたい。
データまわりの定義は厳密に決めていくことが大切だと自分は考えています。
理由としては一度データが入ったデータベースは変更が難しくなってしまうからです。
アプリケーションレイヤーのvalidation制御はもちろん。
DB側での外部キー制約、ユニークキー制約、複数の一意制約、null制約などルールをしっかり決めておくことが大切。
新規開発においては、使用がコロコロ変わるのはしょっちゅうあるのである程度変更があることを前提に開発していくと良い。
ただ、リリース段階まで行く時にはきっちりと制約周りは定義することが守るべきルールとしてある。
Rails, Carrierwave, MiniMagick, Piet, 画像アップロード, 画像圧縮
Railsで画像をアップロードし、かつ画像圧縮も行いたい時の実装をメモで残す。
環境
OSX 10.11.6
Rails 5.0.0
Ruby 2.3
Gem
実装に必要なGemをインストール
gem "carrierwave" gem "piet" gem "mini_magick"
bundle install --path vendor/bundle
Piet
Carrierwaveのextensionとして画像圧縮を行えるようにするGem
圧縮を行う類似のGemがいくつか存在するがPietを採用
トータルダウンロード数 比較
piet : 242,713 carrierwave-imageoptimizer : 97,947
jpegoptim
.jpeg画像の圧縮で使用するパッケージ
brew install jpegiotim
pngquant
.png画像の圧縮で使用するパッケージ
画像圧縮を行うPietではpngquantを用いて圧縮を行えるように他のGemを利用している。
Pietのドキュメントに導入方法が記載
pngquantが圧縮効率が良いのでこちらを採用する。
圧縮効率の比較資料
brew install pngquant
アップローダーの作成
bundle exec rails g uploader image #=> app/uploaders/image_uploader.rb
上記のコマンドを実行しアップローダーを作成します。
class ImageUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick include Piet::CarrierWaveExtension process quality: 80 # Large size process resize_to_limit: [640, 640] process :custom_optimize # Midium size version :midium do process resize_to_limit: [300, 300] process :custom_optimize end # Small size version :small do process resize_to_limit: [200, 200] process :custom_optimize end def extension_white_list %w(jpg jpeg gif png) end def mimetype IO.popen(["file", "--brief", "--mime-type", path], in: :close, err: :close) { |io| io.read.chomp.sub(/image\//, "") } end def custom_optimize case mimetype when "png" then pngquant when "jpeg", "gif" then optimize(quality: 90) end end
custom_optimize メソッド独自で定義し、画像の拡張子によって実行する圧縮を分岐させるような形に。
initialize以下のcarrierwave.rbにも定義
CarrierWave.configure do |config| # hogehoge end
module CarrierWave module MiniMagick def quality(percentage) manipulate! do |img| img.quality(percentage.to_s) img = yield(img) if block_given? img end end end end
流れで画像圧縮のGemを作ったので興味があればこちらも。
リファクタリング:Rubyエディション 読んだ
- 作者: Jay Fields,Shane Harvie,Martin Fowler,Kent Beck,長尾高弘
- 出版社/メーカー: アスキー・メディアワークス
- 発売日: 2010/02/27
- メディア: 大型本
- 購入: 9人 クリック: 321回
- この商品を含むブログ (50件) を見る
最近は「コードの質を高め、生産性の高いコードを書く」ことを1つの目標にしていて、綺麗なコードの書き方や設計周りの本を読んでいます。
「リファクタリング:Rubyエディション」は前々から読んでみたいと思っていたのですが、1冊4800円もするので買うのに少しビビってました。(学生財布)
しかしいざ、買って読んでみるとなんでもっと早く買わなかったんだと。4800円をケチってた自分を叱りたくなりました。
内容はもう目から鱗で。今まで自分が思っていた、考えていたことをうまく言語化してくれていて本当に素晴らしかったです(特にカタログ周り)
メタプログラミングRubyも素晴らしい本でしたが、リファクタリング:Rubyも負けないぐらいいい本です。 Rubyエンジニアの人たちの必読書になると思います。
感想
内容は主に4つの構成
1.リファクタリング -最初のサンプル-
リファクタリングの重要性を理解するためにも最初に大きくサンプルコードが載ってあり非常にわかりやすかったです。 学び始めのとっかかりとしては最高。
1つ1つのリファクタリングの積み重ねでここまで綺麗な構成に変わるものだと、before afterで見せてくれます。
また、UMLでも構成を表現してくれるので処理のフローが理解しやすいです。 実務でも構成だったり、処理のフローを整理したい場合の模範にできるなと思いました。
2.リファクタリングの原則
リファクタリングとはこういうものだよ。という概念的な部分の説明
- なぜリファクタリングするのか?
- なぜリファクタリングすべきなのか?
- いつリファクタリングすべきなのか?
- リファクタリングの恩恵はなにか?
- リファクタリングのデメリットはなにか?
- リファクタリングと設計
- リファクタリングとパフォーマンス
これらをわかりやすく説明しています。
「リファクタリングを導入すると、強調点が変わる。事前設計をすることに変わりはないが、完璧な解を探そうとはしなくなる。まずまず妥当な解であれば良い。」
3.コードの臭い
コードの臭いは、リファクタリングをするべき箇所を臭いという表現で説明しています。 3があるおかげで4のカタログの内容理解がグッと高まります。(バイキルト的な)
4.リファクタリングのカタログ
リファクタリングのテクニックをカタログ形式で示してくれています。
カタログを読むことでテクニックが言語化されてまとめられてとても良かったです。
全て頭に入っているわけではないので、今後リファクタリングを進める中で全てマスターしていきたい。
一覧は覚えたテクニックリスト
メソッドの構成方法
- メソッドの抽出
- メソッドのインライン化
- 一時変数のインライン化
- 一時変数から問い合わせメソッドへ
- 一時変数からチェインへ
- 説明用変数の導入
- 一時変数の分割
- 引数への代入の除去
- メソッドからメソッドオブジェクトへ
- アルゴリズム変更
- ループからコレクションクロージャメソッドへ
- 名前付き引数の導入
- 名前付き引数の除去
- 使われていないデフォルト引数の除去
- 動的メソッド定義
オブジェクト間でのメンバの移動
- メソッドの移動
- フィールドの移動
- クラスの抽出
- クラスのインライン化
- 委譲の隠蔽
- 横流しブローカーの除去
データの構成
- 自己カプセル化フィールド
- データ値からオブジェクトへ
- 値から参照へ
- 参照から値へ
- 配列からオブジェクトへ
- マジックナンバーからシンボル定数へ
- タイプコードからポリモーフィズムへ
- タイプコードからモジュールのextendへ
- タイプコードからState/Strategyへ
- 属性初期化の遅延実行
- 属性初期化の先行実行
条件式の単純化
- 条件文の分解
- 条件分岐の組み替え
- 条件式の統合
- 重複する条件分岐の断片の統合
- 制御フラグの除去
- 条件分岐のネストからガード節へ
- 条件分岐からポリモーフィズムへ