サカナ未遂

プログラミング、筋トレ、子育て

Rubyのsuperの呼び出しについて、モジュールをincludeしてる場合

Rubyで、superを使ってスーパークラスで定義されたメソッドにたどり着きたい場合、モジュールがインクルードされていると期待する動作をしない、というのを学んだ。

例)

module Foo
  def hoge
    puts "Foo!!"
  end
end

class SuperBar
  def hoge
    puts "SuperBar!!"
  end
end

class Bar < SuperBar
  include(Foo)

  def hoge
    super      # Fooのhogeが呼び出される
  end
end

superを使うと、継承元のメソッドを呼び出すもんだと思っていたが、モジュールに同名のメソッドがあると、そちらが使われる。 なので、superで意図しない動きをした場合、includeしているモジュールに同名のメソッドがある可能性も視野にいれる必要がある。

骨伝導イヤホンを買ってしばらくたった

半年くらい前に購入して、すごい買ってよかった思っているこの骨伝導イヤホン

すごい便利なんだけど、この便利に慣れすぎて普通になってきたので、まだ感じてる良いところを書いておこうと思う。 (そのうちあたりまえになって便利さを忘れる前に)

僕が思う骨伝導イヤホンの一番いいところは、耳を塞がないことだと思う。(あたりまえなんだけど) うちは二人の子供がいるのだけれども、食事などを作っている時、子供がテレビを見ていると音楽やポッドキャストを聴くことができない。 かといってヘッドフォンをしながら料理はしたくないし、仮にやったとしても子供が何かやらかした時にすぐ気づけないことになりそう。

そんなとき、骨伝導イヤホンだと、軽いので料理やら洗濯物干してるときもつけてられるし、子供が何か不穏なことをしていても気づくことができる。 僕はそれほど音質についてよくわかってないので、「音質が〜」とか思うこともないし、主に英語のポッドキャストとか聴いていて、聴ければなんでもいいと思っているので、重宝している。

デメリットはもちろんある、バッテリーである。フル充電でだいたい6時間くらいで切れるので、これはちょっと気をつけないといけない。オンラインミーティングに使用するとき、バッテリーの残量に気をつけていないといきなり切れてしまうことになる。

そして寝ながらつかえない。これは構造上しかたないが、頭の後ろの輪っかのとこ(なんていうんだろう?バンド?)があるので、寝転ぶとイヤホンがずれる。 一番何もすることがなく、まれに恐ろしく時間がかかる子供の寝かしつけのときに使えないのは悔しい(そういう用途で作ってないので仕方ないが)。この時間に使えれば長時間の寝かしつけでイライラすることも減りそうなもんだけど。

まあ、そんな気になるデメリットはあるものの致命的ではないので、総じて買ってよかったと思ってる。 性能アップしてバッテリーが20時間くらいになったらまた買い直すと思う。

「THE MODELを読んだ」

Saas開発に携わるにあたって、マーケティングとか営業の知識も仕入れておく必要があるのかなぁと漠然と思っていたけど、社内の営業研修に参加したらこの本がおすすめされていたので読んでみた。

既に動いているものを上手に動かす仕事と、一から何かを作り上げる仕事は天と地ほど違う。一から作り上げる過程に携わった人だけが、後にあの仕事を自分がやったと実感できる。 「再現性」とは? 成功モデルを作り上げる過程で行われた意思決定のプロセスである。それを身につければ環境や条件が変化しても対応ができる。

これはアプリ開発でも当てはまることで、最初にその機能を作った人と、あとから来て修正した人との違いみたいなもの。 作る途中で直面した問題や解決した方法、調べたことなどは自分の中に蓄積されるが、出来上がった後に触る場合は、これが当たり前と思ってしまう。

営業プロセスの管理

マーケティングインサイドセールス、フィールドセールスの分業体制。営業プロセスを分業化する。 分業することで、同じリズムの仕事に集中することができるため、効率が上がる。 また役割を固定することで1つのことに集中でき、徹底的に仕事を覚えることができる、そして次の役割に移ってまた新しい仕事を覚える。 1つの役割に集中させて数多く経験させて、スキルをあげるアカデミーのような側面もある。 これによって未経験から入っても効率よくスキルを向上させることができる。

また分業することで、どの部門がボトルネックになるのかを発見しやすく、対策を打ちやすくなる。 単一責任のクラス設計みたいだな。責務を1つだけにすることで、問題がある箇所を特定しやすいし、変更や改善しやすくなる。 一人ですべての業務をすると、各プロセスのどこかに問題があっても発見しにくい。

ザ・モデルのその先へ

インサイドセールスを当時の日本で行おうとすると、「日本のお客様だったらまず会いに来いと言われる」、、、 これはいまでもありそうで怖い、新型コロナの影響でだいぶ減ったがと思われる。

新規リードが永遠に増え続けることはない

当たり前のようで絶望できな言葉。 しかい放置顧客や商談に至らなかったリード、失注などを再び商談化のプロセルへとリサイクルできれば効果が見込める。

まとめ

(中盤から後半にかけては結構読み飛ばしたが、、、) マーケティングインサイドセールス、フィールドセールス、カスタマーサクセスの職種別の業務プロセス、マネジメントや評価指標についてのことが書いてある。僕はエンジニアなので、そのあたりはサラッと読んだが、自分の会社の営業、マーケもこの本のやり方を参考にしていると思われるので、営業の業務内容や社内会議で使われてる用語などわからないことが理解できるようになったと思う。 ビジョン、ミッション、バリューなど、最近ではどの企業でも掲げてるものがなぜ必要なのか、組織づくりのためのベストプラクティスは何かなど、営業以外の組織の核となるものについても書いてあるので、読み物としても面白かった。

リファクタリング:Rubyエディション 7章 オブジェクト間でのメンバの移動

リファクタリング:Rubyエディション

引き続き7章読んでいく。

7章

メソッドやフィールドをどのオブジェクトに管理させるかは、オブジェクトの設計でもっとも重要な判断の一つである。

この問題に対応するためのテクニックについて学んだ。

メソッドの移動(Move Method)

メソッドが、自分のクラスの機能より他のクラスの機能を利用している場合は、そのメソッドがもっともよく使うクラスに新しいメソッドを作る。

クラスの機能が増え、2つのメソッドが密結合になりすぎたときは、メソッドの移動を行うことを考える。

フィールドの移動(Move Field)

メソッドの移動と同じような観点でフィールドが、自分のクラスより他のクラスからよく使われている場合は、そのクラスに新しいフィールドを作る。

クラスの抽出(Move Field)

クラスの仕事が1つではない場合は、新しいクラスを作って仕事を移す。

クラスは凝縮された抽象として少数の明確な任務を果たすべき

ちょっとした機能だと、既存クラスに追加してしまいがちだけど、本当にそのクラスで仕事させるべき機能なのかを考え、必要に応じて新しいクラスを作る必要がある。

クラスのインライン化(Inline Class)

クラスが大した仕事をしていない場合は、全ての機能を他のクラスに移してから消す。

クラスの抽出の逆 (サンプルのコード、結局戻すんかい)

移譲の隠蔽(Hide Delegate

クライアントがオブジェクト内の委譲クラスを呼び出している場合、委譲を隠すためのメソッドを作りカプセル化する。 カプセル化とは、オブジェクトがシステムの他の部分についてあまり知識がないということ。

forwardableを使うことで、処理を移譲した機能を提供することができる。

横流しブローカーの除去(Remove Middle Man)

移譲の隠蔽でデメリットが出る場合に実施する。 移譲オブジェクトに追加するメンバが増えてくると苦痛になってくる。 その場合、クライアントが直接呼び出したほうがよい。

所感

6章は戦術的なリファクタで、各コードを読みやすく、保守しやすくするテクニックに対して7章はクラス設計に関わる部分。 これらを後から行うの結構きつそう。こういった手法は予め知っておくことで綺麗な設計を心がけることができそう。 また一つのテクニックを説明したあと、次でそれの逆を説明してくれている(「移譲の隠蔽」と「横流しブローカーの除去」など)ので、盲目的にカプセル化だ!とか思わず、こういうパターンもあるし、どっちにするか?と思考できるのがよい。

リファクタリング:Rubyエディション 6章

引き続き読んでいく

3章〜5章は座学的な感じなのでざっと読んだ。 6章から、サンプルコードの改良するための手法の説明になる。

6章 メソッドの構成方法

メソッドの抽出(Extract Method)

コードの断片をメソッドにして、その目的を説明する名前をつける

メソッドの粒度が細かく出来ていれば、他のメソッドからそのメソッドを使える可能性が高くなる。 メソッドを抽出することによってコードがわかりやすくなるなら、抽出されるコードよりも名前のほうが長くてもメソッドを抽出すべき。

ソースコードのコメントはメソッドを見分けるのに使える。 コメント自体が抽出するメソッドの名前の候補になる。

メソッド抽出する対象のコードに、「ローカル変数なし」、「ローカル変数使用」、「ローカル変数への再代入」でそれぞれのケースを考慮する。 - 「ローカル変数なし」簡単 - 「ローカル変数使用」引数を渡す - 「ローカル変数への再代入」変数が抽出したコードより後で使われている場合は戻り値として返す。

メソッドのインライン化(Inline Method)

メソッドの中身が、メソッド名と同じくらいわかりやすい場合は、いちいちメソッド化しなくてよい。

一時変数のインライン化(Inline Temp)

一度だけ代入されてる一時変数は取り除いて式に直す。

一時変数から問い合わせメソッドへ(Replace Temp with Query)

式をメソッドにする。一時変数のすべての参照箇所を式に置き換える。

一時変数は、使われているメソッドのコンテキストの中でしか参照できない、一時変数にアクセスするにはメソッドを長くするしかないので、メソッドの長大化を助長してしまう。

一時変数からチェインへ(Replace Temp with Chain)

チェイニングをサポートするようにメソッドを書き換えて、一時変数を不要にする。 1つのオブジェクトの表現力を高める。

説明用変数の導入(Introduce Explaining Variable)

処理の目的を説明するような名前を持つ一時変数に式、または式の一部の結果を代入する。

確かに処理がわかりやすくなるがReplace Temp with Queryとぶつかりそう??? と思ったらちゃんと書いてた。 「説明変数の導入なら、メソッドの抽出を選ぶ」

一時変数の分割(Split Temporary Variable)

一時変数を使い回さない。代入ごとに別々の一時変数を用意する。 使い回すと変数は複数の仕事をかかえている兆候になる。

引数への代入の除去(Remove Assignments to Parameters)

引数に代入を行っている場合は一時変数にいれる。

メソッドからメソッドオブジェクトへ(Replace Method with Method Object)

ローカル変数の使い方によっては「メソッドの抽出」が適用できない場合、メソッドを独自のオブジェクトに変更し、すべてのローカル変数がそのオブジェクトのインスタンス変数になるようにする。

アルゴリズム変更(Substitute Algorithm)

メソッド本体を新しいアルゴリズムで書き換える

# 変更前
def use_programing_language(langs)
  language_list = []
  langs.each do |lnag|
    if(lang == "Ruby")
      language_list <<lang 
    end
    if(lang == "Java")
      language_list <<lang 
    end
    if(lang == "Javascript")
      language_list <<lang 
    end
  end
  return language_list
end

# 変更後
def use_programing_language(langs)
  langs.select {|lang| %w(Ruby Java Javascript).include? lang }
end

ループからコレクションクロージャメソッドへ(Replace Loop with Collection Closure Method)

ループではなくコレクションクロージャメソッドを使う。

# 変更前
zoo = []
animals.each{ |a| zoo << a.name }


# 変更後
zoo = animals.map(&:name)

サンドウィッチメソッドの抽出(Extract Surrounding Method)

同じようなコードの2つのメソッドで中頃に違いがある場合、重複部分を抽出してブロック付きのメソッドにする。

重複がメソッドの先頭や末尾なら重複を取り除くのはそれほど難しいことではない。 真ん中にある場合はそうもいかないが、Rubyのブロックを使えばうまい具合に重複部分を抽出してメソッドにまとめることができる。

クラスアノテーションの導入(Introduce Class Annotation)

クラス定義からクラスメソッドを呼び出して振る舞いを宣言する。

initializeを隠蔽する?あまり理解できてない。

名前付き引数の導入(Introudce Named Parameter)

引数リストをハッシュに変換して、ハッシュキーを引数の名前として使う。

# 変更前
Class Person
  attr_reader :first_name, :last_name
  
  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
end

Person.new("yamada", "taro")


# 変更後
Class Person
  attr_reader :first_name, :last_name
  
  def initialize(params)
    @first_name = params[:first_name]
    @last_name = params[:last_name]
  end
end

Person.new(first_name: "yamada", last_name: "taro")

呼び出し元は綺麗になるが、クラス定義の方はメソッドの必須引数をしるためにメソッド定義を読む必要がある。 ここで「クラスアノテーションの導入」を使う。

そしたら

Class Person
  attr_reader :first_name, :last_name

  hash_initializer :first_name, :last_name

(どのみちメソッド定義読まなあかんのか?)

所感

サンプルコードを用いて、各テーマに沿ってリファクタリングを学べる。 なぜそれが必要なのかという理由と手順が書いてあり、ありがたい。

とにかく一時変数はさけて、小さいメソッドにしていくことが綺麗なコードにできる。 ただ何がなんでも一時変数がだめではなく、引数に代入をさせないようにするときなどは有用。 6章後半はちょっと理解が難しかったが、ここに書いてあると覚えておけば折を見て見返すことができる。

リファクタリング:Rubyエディションを手に入れた

高かったけど買いました。リファクタリングRubyエディション

リファクタリングとは何か?

リファクタリングは、コードの外から見た振る舞いを変えずに、内部構造を改良するようにして、ソフトウェアシステムを変えていくプロセスである

第1章

ビデオレンタルのコードのリファクタリング リファクタするにはまずテストコードが必要なので、minitestで雑にテストコードを作って、本の通りにリファクタリングしてみる。(サンプルコード、いくつか誤記があったので修正したもの)

github.com

リファクタ後

https://github.com/terachan3700/refactoringRubyEdition/compare/main...refactor-complete

要約

1つのメソッドが長いく多くのことをやりすぎているときは注意が必要。 インタープリタはコードのきれい、汚いを気にしないが、システムに変更を加えるのは人間が関わってくるし、人間はコードがクリーンかどうかに左右される。 新しい機能を追加する場合に、まずリファクタリングして作業しやすくしてから追加するのが望ましい。

リファクタリングの第一歩としてテストコードが必要。 メソッドを小さく分解するたびにテストを実行する。

第2章

リファクタリングの歴史、定義、利点、いつすべきか、問題点など

  • リファクタリングの定義
    リファクタリングはソフトウェアの振る舞いを変えずに、わかりやすく直しやすいものに変更すること。 パフォーマンスの改善のための変更は、最適化と呼ぶ。

  • リファクタリングはソフトウェアをわかりやすくする
    他のプログラマや自分が将来、ソフトウェアに変更を加えるときのために、わかりやすくしておく必要がある。 自分の書いたコードも、ずっとは覚えていられないので、ごちゃごちゃしてるコードだと、読み解くだけで非常に時間がかかる。

などなど。 随時更新していく

スターティングGo言語 5章を読む

スターティングGo言語、5章を読んだ。

スターティングGo言語 (CodeZine BOOKS)

スターティングGo言語 (CodeZine BOOKS)

  • 作者:松尾 愛賀
  • 発売日: 2016/04/15
  • メディア: 単行本(ソフトカバー)

Chapter 5 構造体とインターフェース

ポインタ

Goにはポインタがある。ポインタはメモリ上のアドレスと型の情報

ポインタの定義

「*」を型の前に置くことで定義できる。

var p *int

// *ppのポインタのポインタ
var pp ***int

// &を使って任意の型からポインタを生成できる
i := 100
pointa := &i

fmt.Println(*pointa) // 100が表示される

&をつけると、ポインタを生成できる。 ポインタの示す値を参照するには*をつける。 ポインタ型の変数の前に*を置くことでポインタ型が指し示すデータ本体を参照できる。 この仕組をデリファレンスと呼ぶ。

配列のポインタ

p := &[3]int{1, 2, 3}
fmt.Println(*p)  // [1 2 3] が表示

// 配列の要素を取得(ポインタ型のデリファレンス → 要素の参照)
fmt.Println((*p)[0]) // 1

// これでもいける(ポインタ型のデリファレンス → 要素の参照をコンパイラが置き換えてくれる)
fmt.Println(p[0]) // 1

// この書き方はエラー
fmt.Println(*p[0])

文字列型のポインタ

Goの文字列はイミュータブルで、一度生成された文字列は変更できない。 そのため文字列を構成するbyte型の配列に対してポインタ型を定義できないようになっている。 (ポインタ型を定義できると、文字列に対する破壊的変更を許可することになるため) Goの文字列は。文字列の結合処理を行う度に新しい文字列がメモリ上に作成される。 String型を変数への再代入や関数の引数として使った場合も、文字列の実態が別メモリ領域にはコピーされず、参照先が渡される。

構造体

構造体(struct)は複数の型の値を1つにまとめたもの。 構造体に構造体を含めることもできる。

typeについて

typeはエイリアスを定義することができる。 エイリアス同士の互換性はない。

// intのエイリアスのMyIntを定義
type MyInt int

// int型として使える
var n1 MyInt = 5
n2 := MyInt(7)

// エイリアス型には互換性がない
type T0 int
type T1 int

t0 := T0(1)
t1 := T1(2)

// コンパイルエラー
t0 = t1

構造体の定義

構造体はstructで囲われた範囲で定義し、typeを使って新しい型名をあたえる。

type FullName struct {
    FirstName string
    LastName string
}

var fullName FullName

// 構造体フィールドへの代入
fullName.FirstName = "Yamada"
fullName.LastName = "Tarou"

// 複合リテラルで構造体を生成
fullName2 := FullName{"Suzuki", "Hanako" }

// フィールドを明示的に指定して定義
fullName3 := FullName{ FirstName: "Suzuki", LastName: "Hanako" }

構造体の中に構造体も定義できる。

type Feed struct {
    Name string
    Amount  string
}

type Animal struct {
    Name string
    Feed  Feed
}

a := Animal{
    Name: "Monkey",
    Feed: Feed{
        Name:   "Banana",
        Amount: 10,
    },
}

// ネストした構造体は階層的にたどってアクセスできる
fmt.Println(a.Feed.Amount)

上記のAminal内で定義してるFeed型のフィールド名を省略すると、フィールド名は暗黙的にFeedになる。 階層的アクセスの記述も省略して記述できる。

type Animal struct {
    Name string
    Feed // フィールド名を省略
}

// フィールド名を省略してアクセスできる
fmt.Println(a.Amount)

// 暗黙的に設定されたフィールド名をたどってアクセスもできる。
fmt.Println(a.Feed.Amount)

この挙動は埋め込まれた構造体のフィールド名が一意の場合に限り省略してアクセスできる。

構造体は再帰的な定義はできない。 構造体は値型になるので、関数の引数として渡して、その構造体が関数のなかで書き換えられても元の構造体にはなんの影響もない。

newで指定した型のポインタ型を生成

newを使うとポインタ型を生成できる。

type Person struct {
    Id   int
    Name string
    Area string
}

// ポインタ型になる
p := new(Person)

メソッド

Goのメソッドは任意の型に特化した関数を定義できる。

type Point struct{ X, Y int }

// Point型へのメソッド追加
func (p *Point) Render() {
    fmt.Printf("<%d, %d>\n", p.X, p.Y)
}

// Point型のpを生成
p := &Point{X: 5, Y: 12}

// pは定義したメソッドを呼び出すことができる
p.Render()

構造体で定義した変数名とメソッド名が同名だとコンパイルエラーになる。 同名のメソッドでもレシーバ名が異なっていれば定義できる。

型のコンストラク

構造体を初期化するために「型のコンストラクタ」というパターンが使用できる。 コンストラクタはNewXXXというイディオムがよく利用される。

メソッドを定義するときはレシーバはポインタ型にすべき

メソッド定義時にレシーバが値型の場合、呼び出したときにレシーバそのもののコピーが発生するため、メソッド呼び出し側とメソッド内部でレシーバの実態がことなることがある。またレシーバがポインタ型でも呼び出せてしまうため、挙動が異なることがある。

タグ

タグはフィールドにメタ情報を付与する機能。 メタ情報のため、プログラムの実行には影響はない。

// 各フィールドにタグをつける
type User struct {
    Id   int    `json:"user_id"`
    Name string `json:"user_name"`
    Age  int    `json:"user_age"`
}

u := User{Id: 1, Name: "Taro", Age: 32}
bs, _ := json.Marshal(u)
fmt.Println(string(bs))

// タグがjsonのキーとして表示される
// {"user_id":1,"user_name":"Taro","user_age":32}

インターフェース

インターフェースは型の一種であり、任意の型が「どのようなメソッドを実装すべきか」を規定するための枠組み。 インターフェースはメソッドの型だけを記述した型となる。 構造体を定義し、インターフェースで定義したメソッドを実装する。 異なる構造体でも共通のインターフェースをもたせることで共通の性質を付与できる。

// Animalインターフェースを定義
type Animal interface {
    Bark()
}

// Monkey構造体を定義
type Monkey struct {
    Name string
}

// インターフェースで定義されたメソッドを定義する
func (m Monkey) Bark() {
    fmt.Println(m.Name)
}

// Lion構造体を定義
type Lion struct {
    Name string
}

// インターフェースで定義されたメソッドを定義する
func (l Lion) Bark() {
    fmt.Println(l.Name + "!!!!")
}

func main() {
    var animal Animal

   // インターフェースに格納し、実行
    animal = Monkey{"Taro"}
    animal.Bark()

        // インターフェースのメソッドを実装していれば、どんな型も格納できる。  
    animal = Lion{"スカー"}
    animal.Bark()

}

所感

5章までで構文的なことは網羅してそう。6章以降はGoのツールやパッケージについてなので必要に応じて読んでいこうと思う。 DBを使うとかAPIの実装とかまだまだよくわかってないとこがあるので、次はGoプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る impress top gearシリーズでも読んでいこうかと思う。