T_Y_CODE

プログラミング学習記録

学習記録 8日目

8日目の学習記録をまとめていきます。

学習計画

学習内容

オブジェクト指向設計実践ガイド 2章

  • クラス内のメソッドのコードは簡単に変更できるものであるべき。変更が簡単であることを次のように定義する。
    • 変更は副作用をもたらさない
    • 要件の変更が小さければ、コードの変更も相応して小さい
    • 既存のコードは簡単に再利用できる
    • 最も簡単な変更方法はコードの追加である。ただし、追加するコードはそれ自体変更が容易なものとする。
  • 上記定義より書くべきコードとは以下のようにあるべきである。
    • 見通しが良い(Transparent): 変更するコードにおいても、そのコードに依存する別の場所のコードにおいても、変更がもたらす影響が明白である
    • 合理的(Reasonable): どんな変更であっても、かかるコストは変更がもたらす利益にふさわしい
    • 利用性が高い(Usable): 新しい環境、予期していなかった環境でも再利用できる
    • 模範的(Exemplary): コードに変更を加える人が、上記の品質を自然と保つようなコードになっている
  • (それぞれの頭文字をとって)TRUEなコードは現在のニーズを満たすだけでなく、将来的なニーズを満たすように変更を加えることもできる。
  • TRUEなコードを書くためにはそれぞれのクラスが明確に定義された単一責任なクラスになるよう徹底すること。
  • 以下自転車とギアを例に設計を行っていく
  • 自転車のギアとギア比をまとめたRubyスクリプトを以下にまとめる。
chainring = 52
cog = 11
ratio = chainring / cog
puts ratio # => 4.727272727273

chainring = 30
cog = 27
ratio = chainring / cog
puts ratio # => 1.111111111111
  • 対象領域(ドメイン)内を見ると「自転車」や「ギア」という単語が目に付く。範囲の大きめな「自転車」クラスを作りそうになるがギア(データ)とギア比(振る舞い)のみ扱っている上記スクリプトは「ギア」クラスが適当。
class Gear
  attr_reader :chainring, :cog
  def initialize(chainring, cog)
    @chainring = chainring
    @cog = cog
  end

  def ratio
    chainring / cog
  end
end

puts Gear.new(52, 11).ratio # => 4.727272727273
puts Gear.new(30, 27).ratio # => 1.111111111111
  • 上記クラスに対し、変更が加わるとする。以下公式からギアインチを算出する振る舞いを追加する。
    • ギアインチ = 車輪の直径 × ギア比
    • 車輪の直径 = リムの直径 + ( タイヤの厚み × 2 )
class Gear
  attr_reader :chainring, :cog, :rim, :tire
  def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @rim = rim
    @tire = tire
  end

  def ratio
    chainring / cog
  end

  def gear_inches
    ratio * ( rim + ( tire * 2 ) )
  end
end

puts Gear.new(52, 11, 26, 1.5).gear_inches
# => 137.09090909
puts Gear.new(30, 27, 24, 1.25).gear_inches
# => 125.27272727
  • 上記クラスが単一責任かを確認します。
    • クラスの持つメソッドを質問に言い換えて見る。違和感があればそのメソッドはクラスに見合っていない。
    • クラスが何をしているかを1文で説明してみる。「それと」「または」が付けば単一責任ではない。
  • オブジェクト指向設計者は凝縮度という言葉を使いこの概念を表す。単一責任の法則(SRP)は責任駆動設計(RDD)という概念に由来する。クラスがすることはすべて、そのクラスの目的に強く関連することを求める。
  • Gearクラスは「自転車へのギア比の影響を計算する」という振る舞いはSRPに則っているがtireデータは明らかにGearとは関係がない。よってGearクラスは2つの責任を持ってしまっている。
  • 単一責任にするのはクラスだけではない。メソッドも単一責任にする必要がある。以下はそのリファクタリング例。
# 配列を繰り返し処理し、かつ1つの車輪の直径を計算する。
def diameters
  wheels.collect { |wheel| wheel.rim + ( wheel.tire * 2) }
end
# 配列を繰り返し処理する
def diameters
  wheels.collect { |wheel| diameter(wheel) }
end
# 1つの車輪の直径を計算する
def diameter(wheel)
  wheel.rim + ( wheel.tire * 2)
end
  • 同様にGearクラスのgear_inchesも変更出来る。ger_inches内の計算式には車輪直径を計算している式がある。これは別メソッドで定義すべき。
class Gear
  attr_reader :chainring, :cog, :rim, :tire
  def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @rim = rim
    @tire = tire
  end

  def ratio
    chainring / cog
  end

  def gear_inches
    ratio * diameter
  end

  def diameter
    rim + ( tire * 2 )
  end
end

puts Gear.new(52, 11, 26, 1.5).gear_inches
# => 137.09090909
puts Gear.new(30, 27, 24, 1.25).gear_inches
# => 125.27272727
  • diameterメソッドはWheelの直径でありGearとは無関係である。新しくWheelクラスを作るのもいいがWheelに対しまだメソッド数が少なく費用(作業時間)対効果が少ない用に感じる。新しくクラスを作る課題は先にとっておき今はRubyのStructクラスを用いてGearクラス内で対応する。
class Gear
  attr_reader :chainring, :cog, :rim, :tire
  def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @wheel = Wheel(rim, tire)
  end

  def ratio
    chainring / cog
  end

  def gear_inches
    ratio * wheel.diameter
  end

  Wheel = Struct.new(rim, tire) do
    def diameter
      rim + ( tire * 2 )
  end
end

puts Gear.new(52, 11, 26, 1.5).gear_inches
# => 137.09090909
puts Gear.new(30, 27, 24, 1.25).gear_inches
# => 125.27272727
  • 現在のプログラムに更に車輪の円周を求める機能を追加してほしいと言われたとする。Wheelクラスを作る機会が来ました。
class Gear
  attr_reader :chainring, :cog, :wheel
  # ギア比のみの計算が出来るようにwheelは初期値nil
  def initialize(chainring, cog, wheel = nil)
    @chainring = chainring
    @cog = cog
    @wheel = wheel
  end

  def ratio
    chainring / cog
  end

  def gear_inches
    ratio * wheel.diameter
  end
end

class Wheel
  attr_reader :rim, :tire
  def initialize(rim, tire)
    @rim = rim
    @tire = tire
  end

  def diameter
    rim + ( tire * 2 )
  end

  def circumference
    diameter * Math.PI
  end
end

@wheel = Wheel.new(26, 1.5)
puts wheel.circumference
# => 91.196186954
puts Gear.new(52, 11, @wheel).gear_inches
# => 137.09090909
puts Gear.new(52, 11).ratio
# => 4.7272727272
  • どのクラスも単一責任になっており完璧かは分からないが十分に良いコードといえる。

Linux標準教科書 6章

  • viはページャであり、エディタである。viでファイルを開くとコマンドモード(viewモード)という行の削除, コピー, カット, ペーストなどの編集操作を行うことが出来るモードになる。他に、文字入力を受け付けるインサートモードがある。
    • インサートモードへの切り替えはiを入力する。
vi <ファイル名>
  • ファイルを閉じるには:qを打ち込む。
  • ファイルを保存するにはESCキーを押した後に、:wを入力する。
  • ファイルを保存して終了するにはESCキーを押した後に、:wqを入力する。
  • ファイルを保存せずに強制終了するにはESCキーを押した後に、:q!を入力する。
  • viコマンドはスクリーンエディタと呼ばれ、基本的にカーソルがある位置に対してコマンドを実行する。
  • コマンドモードでの便利なコマンドは以下の通りである。
    • aコマンドでカーソルより前に文字入力が出来る。
    • Iコマンドで行の先頭にカーソルを持ってくる。
    • Aコマンドで行の末尾にカーソルを持ってくる。
    • カーソルの上下左右はそれぞれkjhlキーに対応している。慣れるのに時間がかかりそう...
    • 0コマンドでカレント行の行頭へ移動することができる。
    • $コマンドでカレント行の末尾へ移動することができる。
    • :nコマンドでn行目まで移動する。
    • ggコマンドで1行目まで移動する。
    • Gコマンドで文末まで移動する。
    • xコマンドで1文字削除(カット)
    • ddコマンドで1行削除(カット)
    • yyコマンドで1行コピー
    • nyyコマンドでn行コピー
    • pコマンドでカーソルの次にペースト
    • Pコマンドでカーソルの前にペースト
    • uコマンドでカット, ペーストのアンドゥ
  • コマンドモードで文字列を検索するには/コマンドを使用する。nコマンドで次の検索, Nコマンドで前の検索を行います。
/文字列
  • コマンドモードで文字列の置換を行うには以下のコマンドを用いる。
:ns/検索文字列/置換文字列/オプション
// 1行目の最初のBRをbrに置換
:1s/BR/br
// 1行目の全てのBRをbrに置換
:1s/BR/br/g
// ファイル全体の全てのBRをbrに置換
:%s/BR/br/g
// 置換の度に確認を行う
:%s/BR/br/gc

リーダブルコード 13章

  • 要望に対して必要以上のコードを書いてはいけない。コードは常に最小限であるべき。
  • プロジェクトが進んでいくとコードは複雑化していく。コードはできるだけ小さく軽量に維持する。
    • 汎用なユーティリティコードを作り、重複コードを削除する。
    • 未使用のコードや無用の機能を削除する。
    • プロジェクトをサブプロジェクトに分割する。
    • コードの重複を意識する。軽量で機敏にしておく。
  • 標準ライブラリで解決できる可能性もある。APIには目を通しておくこと。

スッキリわかるSQL入門 1章

  • 現在様々な分野で使用されている複数のテーブルでデータを管理するデータベースをリレーショナルデータベース(RDB)という。
  • RDBは複数のテーブルが入っている。テーブルには名前が付いておりテーブル名という。テーブルは列(column)と行(row)で構成されている。
  • 1つの行が1件のデータに対応し、列はそのデータの要素に対応する。
  • SQLはデータベースファイルにではなくデータベース管理システム(DBMS)に命令を送っている。
  • DBMSのうち、複数の表の形式でデータを取り扱うものを特にRDBMSという。
  • MySQLPostgreSQL, SQLiteRDBMS
  • DBMSによってSQLの命令や記法は異なるが基本的な構文や考え方は同じ。
  • SQLでデータベースから情報を検索するには以下の命令文を使用する。
SELECT <列名(,区切りで複数可)>
FROM <テーブル名>
WHERE <条件式> 
  • テーブルにデータを追加するには以下の命令文を使用する。
INSERT INTO <テーブル名>
(<,区切りで入力する列データを入力>) -- 省略可
  VALUES ( <,区切りで各列のデータを入力。数値以外はシングルクォートで囲うこと> )
  • テーブルのデータを更新するには以下の命令文を使用する。
UPDATE <テーブル名>
SET <列名> = <入力値>
WHERE <更新する行を特定する条件式>
  • テーブルのデータを削除するには以下の命令文を使用する。
DELETE FROM <テーブル名>
WHERE <更新する行を特定する条件式>

スッキリわかるSQL入門 2章

  • SQLは命令文の途中に改行を入れることが出来る。以下の2文は同意である。
/* 1行で記載 */
SELECT 費目, 出金額 FROM 家計簿 WHERE 出金額 > 3000
/* 改行して記載 */
SELECT 費目, 出金額
FROM 家計簿 
WHERE 出金額 > 3000
  • テーブルの列にはデータ型が定められており、異なるデータ型が入力されようとするとエラーが発生する。
  • SQLのほとんどのデータ操作はSELECT, UPDATE, DELETE, INSERTで事足りてしまう。この4つのSQL命令をDMLと呼ぶ。
  • 「SELECT *」で全ての列の取得が可能。
  • SELECT文における列名やテーブル名は「AS <任意のキーワード>」で別名として取得ができる。

本日の総括

  • vimを初めて使用した。コマンドモードは便利な機能がいっぱいあるので慣れれば強力なツールになりそう。マウスでの操作が煩わしいと感じることもあるのでTerminal上での操作は少しずつ使っていきたい。
  • オブジェクト指向設計実践ガイドとリーダブルコードがちょうど同じような内容だった。
  • Railsチュートリアルで知識不足に感じていたSQLの学習に取り掛かり始めた。少しずつSQLが使えるようになりたい。