Singletonパターン

Singletonパターンについて書いてみます。

Singletonパターンはインスタンスが1つしか存在しない唯一のオブジェクトを生成するために使います。

オブジェクトが1つしか必要ないというケースとは ・スレッドプール ・キャッシュ ・ダイアログボックス ・プリファレンス ・レジストリの設定を処理するオブジェクト ・ロギング用のオブジェクト など。 これらのオブジェクトを二つ以上インスタンス化してしまうとプログラムの誤操作、リソースの使いすぎ、つじつまの合わない結果などの問題が出てきます。

特徴としてクラス外部からインスタンスを作成できないようにするためにコンストラクタをprivateにします。

・Singleton.php

<?php
class Singleton
{
    private static Singleton $singleton;

    private function __construct()
    {
        echo "create singleton";
    }

    public static function getInstance(): \\Singleton
    {
        if (!isset(self::$singleton)) {
            self::$singleton = new Singleton();
        }

        return self::$singleton;
    }
}

・index.php

<?php
require_once 'Singleton.php';

$singleton1 = Singleton::getInstance();
$singleton2 = Singleton::getInstance();

if ($singleton1 === $singleton2) {
    echo "同じインスタンス";
}

// new Singleton();
// Fatal error: Uncaught Error: Call to private

インスタンスをnewしようとするとコンストラクタがプライベートなのでFatal errorとなります。 インスタンスが一つしか存在しないことを保証できます


(参考)
Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本
2005/12/2
Eric Freeman (著), Elisabeth Freeman (著), Kathy Sierra (著), Bert Bates (著), 佐藤 直生 (監訳), 木下 哲也 (翻訳), 有限会社 福龍興業 (翻訳)
5章 Singletonパターン:唯一のオブジェクト

SQLパフォーマンス詳解を読んだ

明けましておめでとうございます。今年もよろしくお願いいたします。

11月の事ですが、SQLパフォーマンス詳解を読みました。
感想などを書いていきたいと思います。

読んでみようと思ったきっかけ
かなり多くの回数呼ばれるプログラムのデバッグや後輩のコードレビューをする中でパフォーマンスを意識したSQLの知識が皆無に等しくこのままでは良くないと思ったため。また先輩から勧められたため。

感想
インデックスをどう活用するかを書いてくれててわかりやすかった。どうすればパフォーマンスが良くなるのかということでググると、「適切なインデックス」を張るということをよく目にするがいつも「どうすれば適切になるんだ」と思っていた。張り過ぎもよくないと聞くので「過ぎ」ってどの程度のことなんだろうと思ったりしていた。インデックスは作成されているけど、sqlの書き方のせいで使えていないとかもある。そういうものがわかってきたので読んで良かったと思います。MySQLのExtra列については今後しっかり勉強したい。

以下まとめ(MySQLのみ) 第4章、5章は省略

第1章 インデックスの内部構造

インデックスの最も重要な目的は、インデックスを張ったデータに対して順序をつけてアクセスできるようにすることです。

writeの処理(insert, update, delete)が走る際に、大量のデータを動かすわけにはいかない。そういう場合の解決策として双方向連結リストでインデックスリーフノードと対応するテーブルのデータを対応させている。

検索ツリー(Bツリー)
データベースがリーフノードを高速に見つけるのに役に立つ。MySQLの場合は、BTREEと表記されている。Bはバランスの略でその名の通り木構造。1~1000というidが入ったノードと1001~2000、2001~3000といったノードがあって20xxというidを検索するとき、1から順に検索しなくて良いので速くなる。1001以上かどうか、2001以上かどうかなどを調べて該当のノードの中を確認する。 ツリーが深さは対数的に増え、リーフノードの増加に対してツリーの深さの増大は非常に遅い。

インデックスによる検索は、以下の3ステップで行われる。

  1. ツリーを走査
  2. リーフノードチェーンをたどる
  3. テーブルからデータを読み出す

UNIQUEなSCANは1しか行わないため高速。

第2章 WHERE句

前提としてSQLを発行する際には実行計画(EXPLAIN)は必ず行う。 MySQLにおける実行計画で最低限注意して見るべき点は下の記事を参考にしています。

https://nippondanji.blogspot.com/2009/03/mysqlexplain.html

複合インデックス定義する際に考えるべき最も重要なのは、そのインデックスを使えるSQL文ができるだけ多くなるように、列の順番を決めることです。

a, b, c, dという4つの列の複合インデックスがあった場合は、a,bを条件に検索するときも使えるので先に定義しておく。cを条件にすることが多い場合は、c, a, b, dなどの順で定義するといい。こうすることで新たなインデックスの作成を減らせる。インデックスの数が多いと更新処理のパフォーマンスに影響する。

WHERE句はインデックスの列の順に合わせて使う。 OR検索やWHERE INなどは場合によっては実行計画が変わるので出来るだけUNIONやJOINをうまく使って出来るだけ使わないようにしたほうがいいかもしれない。データ数が少ないテーブルの場合はそんなに気にしなくていいとは思うが。

パラメータ化クエリはちゃんと使う。SQLインジェクションを防ぐために。残念ながらMySQLはされていないが、SQLによってはパラメータ化クエリを使うことでキャッシュされたSQLが使える。

・範囲検索(大なり、小なり、BETWEEN) なるべく先に狭い範囲になるように検索してから残りを検索する。

・LIKE ワイルドカード(%)を先頭に出来るだけ使わない。 使ったことなかったけどmatchやagainstを使えるなら使う。

カラムはしっかり指定して*で取得するのはやめる。

インデックスは2つ使うより、1つだけ使う方が高速

第3章 パフォーマンスとスケーラビリティ

データ量が多くなるにつれパフォーマンスは悪くなる。

注意深い実行計画の調査結果は、うわべだけのベンチマークよりも信用のおけるものです。完全な負荷テストは意味のあることですが、そのための手間はかかります。

本番のデータとテスト時のデータ、開発時のデータは全く違うものだと思うし、似たようなテストをするにしても大変な手間がかかる。大量データの場合は特に、適切なインデックスが張られているかを注意する。ただそれだけデータがあるということはそれだけ更新処理があると思うので張りすぎにも注意する。

リレーショナルSQLデータベースかリレーショナルでないシステムに関わらず、正しいインデックスを作ることは、クエリの応答時間を短くする唯一の方法です。

第6章 ソートとグルーピング

order by句がインデックスによる順序付けと一致している場合、 データベースは明示的なソート処理を省略できます。

order by句でASK, DESCが混在している場合はインデックスが効かない。ASC,ASCやDESC,DESCだったら効く。混在させる場合はインデックスを定義する必要がある。

第7章 部分的結果

・LIMIT

データベースは、部分結果のみを取得することを事前に知っている場合のみ、部分結果のみ、部分結果向けにクエリを最適化できます。

実際に事前に知れているかどうかは実行計画を確認する。ただMySQLの場合は、Extra列に「using filesort」がなければ部分結果の取得が行われている。

・ページングについて

ページングの際は、並べ替えの順序は確定的である必要があります。

ページングの際はoffsetを使うのが一般的だがそのテーブルにリアルタイムで行が挿入される場合は、挿入されたデータが最初に来る場合があるので、ずれてしまう。

第8章 挿入、削除、更新

インデックスが作られるとデータベースはインデックスを自動的にメンテナンスする。 簡単にいうとinsertするとインデックスのノードも更新するため処理が重くなる。 インデックスを張りすぎると良くないのはこのため。insertが圧倒的に多いテーブルにはインデックスは出来るだけ少なくする必要がある。

インデックスは注意深くかつ慎重に使い、かつ、可能な限り冗長なインデックスは使わないようにしましょう。これは、delete文やupdate文を使う際にも同じ事が言えます。

deleteやupdateにも実行計画はあります。

Smartyでクラス定数を使いたい

表題みたいなことがあります。

下は 物凄く雑ですがクラス定数を宣言

namespace Mental;

class Menhealer {
    const YABAI = 1; 
    const MURI = 2;
    const MOUIYA = 3;
    const KIDUITE = 4;
    const WAKATTEYO = 5;
}

これをSmarty側で呼び出す方法は以下です

{"Mental\Menhealer::YABAI"|constant}

if文だとこう書くらしいです。

{if $menhealer === constant("Mental\Menhealer::KIDUITE"}
    努力はしている
{/if}

クラス定数使うとわかりやすいですが、Smartyだと今のところuseが使えないのもあって長くなるのが難点ですね。

なぜあなたはJavaでオブジェクト指向開発ができないのかを読んでみました

なかなか挑戦的なタイトルの本です。

なぜ僕はJavaでオブジェクト指向開発が出来ないんでしょうか。
その謎を解き明かしていきたいと思います。
Javaでと書くとJava以外だと出来るみたいに見えますが出来ませぬ…

全体の感想

モデリングの考え方が特に参考になった。
・仕様を見て何をクラス化するのか
・そのクラスから生み出されるインスタンスにどういう属性を持たせるか
・どういう操作を持たせるか
は時間をかけてしっかり吟味する。

吟味するためにも仕様はしっかりと書き出す。
モデリングにただ一つの正解はない。
自分ではわかりやすいつもりでも他の人から見たらわかりにくいかもしれないことは頭に入れておく。
デザインパターンを使えるといいのはこういうところだと思う。

コラムも凄く良かった。

ただオブジェクト指向プログラミングに全く触れてない人だと難しいかもしれない。
オブジェクト指向プログラミングの入門書を読んでみて、その入門書に書いてあることはなんとなく理解できるけど実際の現場でどう使うのかがわからないとか、使えているかどうか自信がない人向けだと思う。

今後の自分の課題

  • クラス設計を簡易にするために難しい例でも仕様をしっかりと書き出せるようになること(ユースケースをマスターする)
  • UML図を使いこなせるようになること
  • メモリ管理を意識したプログラムを書けるようになること

以下簡単なまとめ

Lesson 1 オブジェクト指向をなぜ難しいと感じるのか

オブジェクト指向は、ソフトウェアを効率的に開発するために生まれた技術
効率的というのは拡張とか再利用しやすいとかってことだと思う。

プログラミングの手順は、3つのステップに分かれている

  1. コンピュータに行わせたいことを理解する
  2. 理解したことを説明できるレベルまで整理する
  3. コンピュータにわかる言葉に翻訳する

プログラミングができない理由は1と2が出来ていないことが多い。
自分が先生とコンピュータが生徒という例えはしっくりきた。
人に何かを教えることができている = 自分が理解できている
1、2が出来ていないというのは自身がコンピュータに教える内容を理解できていないということだと思う。

Lesson 2 オブジェクト指向は本当に必要なのか

プログラミングにおいて最も難しいのは、仕様のわかっているソフトウェアを最初から作ることではありません。一度作ったソフトウェアに対して変更や機能追加をしていくことなのです。

全くもってその通りだと思うし、以前自分が書いたコードを最近後輩に触ってもらうことがあって修正しにくいコード修正させているなあと思った。
ほんとごめんなさい笑

極論を言うと変更、機能追加がなければプログラムはどんなに見にくくても要求通り動いてさえいればいいと思う。
変更、機能追加があることの方が多いので変更や機能追加がしやすくなるように初期の段階から考えていかなければならない。
ただし、

  • 既にそうなっていないもの
  • 初期の段階では想定できなかったもの
  • 想定して開発したつもりが後々手を付けてみると変更が難しかったもの
  • 時間の制約によりそこまで手が回らなかったもの

等に関しては後々適切にリファクタリングを行いたい。

一度作成されたプログラムの変更は根本的に難しい

これもっと世に広まってほしい…簡単と思われると辛い…笑
出来るだけ初期の段階で良いものを作りたい

Lesson 3 オブジェクト指向でのソフトウェア開発

  • オブジェクト指向とは人間の言葉をできるだけそのままコンピュータへ伝えるための方法として考えられたもの
  • オブジェクト指向で度々使われる操作とはメッセージをきっかけとしたオブジェクトの振る舞いのこと

余談だがコード例の下の辺りはenumを使って管理した方がいいとeffective javaに書かれていた気がする。

public static final int STONE = 0; // グー
public static final int SCISSORS = 1; // チョキ
public static final int PAPER = 2; // パー

Lesson 6 より複雑なソフトウェアの作成

オブジェクト指向で重要なのは明確に役割を分担すること
現実世界にできるだけ合わせるのはそうしたほうがわかりやすいためでありマストではない。
あくまで役割を明確に分担が最優先

モデリング手順

  1. 仕様を決める
  2. クラスを抽出する
  3. オブジェクト間のメッセージを考える
  4. 各クラスの操作を洗い出す
  5. 各クラスの属性を洗い出す

■仕様を決める
仕様を決定する段階ではユースケースという手法が一般的。

ユースケースとは
ユーザがソフトウェアをどのように利用するかというシナリオを文章として記述する。
ユースケースを書くことによって、ソフトウェアを使う側の視点に立って、それが「どうあるべき」を明確に整理することができる。

■クラスを抽出する
絶対的な正解があるわけではない。
どれをクラス化してどれをしないかをしっかり考える。
クラスの抽出に関しては、後に行う洗い出しの最中にまた戻って分析した方がいいこともある。

■オブジェクト間のメッセージを考える
以下の3点を考える

  • どのクラスからどのクラスへメッセージを送るのか
  • どのようなメッセージを送るのか
  • メッセージを受け取った結果、どのように振る舞うのか

クラス間に関連がある = それらのクラス間で何らかのメッセージをやり取りする可能性がある。

・各クラスの操作を洗い出す
そのインスタンスがどういう振る舞いをするのか

・各クラスの属性を洗い出す
外部に影響しない属性は不要

1〜5のモデリングが終わったらクラス図にまとめる。

Lesson 7 既存のソフトウェアの再利用
Lesson 8 再利用を考慮したソフトウェアの設計
Lesson 9 フレームワークを利用したソフトウェア開発

オブジェクト指向を使ってソフトウェアを効率的に開発していくには?
継承とか移譲とか自作フレームワークとかを使って共通点をモデリングしていく

例えばトランプの場合、全部でカードが54枚ということは共通している
ゲームによってはジョーカーを使用しない場合があるので52枚とジョーカー2枚でそれぞれクラス化した方がいいのかもしれない。
手札を使うゲーム(ババ抜き、大富豪)、使わないゲーム(神経衰弱)などもあるので、ここでも分けることもできる。
継承(is-a)や移譲(has-a)をうまく使っていく。
継承は誤るとわかりにくくなるので注意

モデリングのキレイなプログラムがよいプログラムがよいプログラムか? → △
近年だとコンピュータの処理速度が高くなっているため、拡張性・再利用性ばかり重視されて実行速度はあまり気にされないことが多い。
とはいえ無駄なループ、無駄なオブジェクトの生成はしないようにしっかり注意する。

結論:自分がJavaでオブジェクト指向開発が出来ない理由

モデリングが雑すぎることが一つの大きな原因だと思った。
振り返ってみるとモデリングを雑に起こっているがために
コーディング中にこのメソッドが足りなかっただの、これはいらなかっただの
ってことがある。
さらにいけないのがそれをその場で修正して結果的にそのクラスのインスタンスに相応しくない属性やメソッドが入っていたりするケースがあった。
もちろんプルリク通らず修正し直した。

今後は、
・自分がコンピュータに説明できるまで理解できているか
・Lesson 6 にあったモデリング手順の実践
・コーディング中にモデリングが甘かった部分が出てきたら手順を振り返ってクラス図を見ながら吟味する。
を意識して開発を行っていきたいと思った。
モデリングするための課題も見えたのであわせて学習していく。

エンジニアのための時間管理術を読んでみました

業務効率改善のために「エンジニアのための時間管理術」を読みました。
感想などを書いていきたいと思います。

呼んでみようと思った理由
タスク管理ツールなどを使ってなんとなくでやっていた時間管理を
一度体系的に学んでベターな時間管理をしたいと考えたから。

感想
タイムマネジメントって難しいと改めて感じた。
今抱えているものだけでなく
急に厳しい期限で新しいタスクが入ってくることがあるというのは
タイムマネジメントを難しくする要因の中で上位だと思う。
1ヶ月先までの期限の仕事を後回しにしていて、
別の優先度が高い作業を始めて急に最優先でやらなければ仕事が入ってきて
後回しにしていた作業もあって地獄を見るなんてのは…考えたくないです。

本書で例えばプログラムを書いている場合に
メールが来ること、
突然話しかけられること、
チャットが来ることなどを
じゃまが入るというどストレートな表現してたのは面白かった。

作業を完了するには中断なしで丸1日、
実際には1ヶ月という言葉も面白かった。
他の人の仕事も実際こんな感じなんだろうと思う。
自分から見て他の人の作業が「その作業もっと早く終わるはずなのに」
というものがあってもその人なりの事情があるのだと感じた。
その事情を把握して自分にできることがあれば手助けしたいし、
逆に自分も事情を説明してどうすれば早くなるかを相談したい。
またじゃまにならないような割り込ませ方も考えたい。
新しい仕事を振る時はこの時間の時にするとか。

実はプログラマ向けの本ではないとばっさり書かれているが、
自分でも当てはまったし、読んだ感じ社会人向けの本として有用だと思う。
完全なプログラマ向けのやつがあるなら読んでみたいと思った。

自分みたいにあまりちゃんと時間管理をやってなかった人が
本書に載っていることを全部やってみるのは困難だと思う。
無理のない範囲で取り入れていきたい。

以下章ごとのまとめ、プチ感想など

1章 タイムマネジメントの原則
原則として以下の6つがある。

  1. タイムマネジメント情報を1つの「データベース」にまとめる
    タスク管理ツールなどを色々使わない。一つにする。
  2. 能力は重要な作業のために温存しておく
    プロジェクトAを行っている場合はそれ以外のことは考えない、忘れる。
    プロジェクトBのことが気がかりな場合、その気がかりをメモしておいて脳では忘れる。
    やっている作業のこと以外は考えない
  3. 日課を定め,それらに従う
  4. 習慣やモットーを養う
  5. 「プロジェクトタイム」の間は集中力を保つ
  6. 日常生活の管理にも,仕事で使用するのと同じツールを使用する
    日常で練習して仕事に活かすのもあり

2章 集中と割り込み
集中しやすい環境を作る。
メーラーは基本的に閉じて2〜3時間おきにみる。
迷ったら捨てる、閉じる。
自分の場合は開きっぱなしが多すぎる。
自分の集中力のピーク時を認識し、その時間に最重要タスクをこなす。
基本はマルチタスクが当たり前だが、
ストレスが溜まっている時、睡眠不足の時は避ける
割り込みされた時は委任することも大事
委任できなかったら諦めて頑張りましょう。

3章 ルーチン
出来るだけ余計なことを考えないようにルーチンは作れるなら作るべき
考えることは重要な仕事に回す。
ただ定期的にルーチンの見直しはやったほうがいいと思う。

4〜7章 サイクルシステム
自分の記憶力を信用しない
サイクルシステムを作成する。

  1. 365日分の作業リスト(作業日誌)
  2. 今日のスケジュール
  3. 約束のカレンダー
  4. メモ(人生目標などを記載)

そして,毎日を以下のサイクルで繰り返す.
・今日のスケジュールを作成する
・今日の作業リストを作成する
・優先順位を付け,スケジュールを調整する
・予定に取り組む
・1日の終わり
・会社を出る
・繰り返す

結構大変そうなので最初は緩く始めてもいいかも

目標を立てないと
「単純に作業をうまく進められるようになるだけ」
になってしまう。
・”何を” 実現したいのか
・”いつ” それを実現したいのか

以下の期間で立てる
・短期(1ヶ月)
・中期(1年)
・長期(5年)

こういうのが苦手なのはなんとかしたい、特に長期。

8章 優先順位
自分で優先順位を決める。
ただ上司が考える優先順位とは異なっているかもしれないのでその辺りは確認する。
自分は今、Aという仕事とBという仕事があって~な理由でAを優先して行うつもりですけどよろしいですか等

9章 ストレスの管理
自分に合ったストレスの管理方法、発散方法を見つける。
自分は体の健康が心の健康につながると思うので運動を継続的に行う。
自分の場合はマッサージはその時は楽になるけど応急処置感があってすぐに戻ってしまう。

10章 電子メールの管理
不必要なメールはどんどん消す。
検索があるとはいえ多いことにいいことはない
メーラーはこの時間に開くなどを決めておく
メールが来たらすぐ返さないといけないことがある場合は難しいけども…
社内チャットツールなども緊急の場合は口頭で伝えてもらうようにお願いして2、3時間おきに見るとかでいいと思う。

11章 時間の浪費
水漏れをしている床を毎回吹くのではなく水漏れしている原因に対処するというのはいい例えだと思った。
根本を解決することに時間を費やす。

仕事上に対しての時間の浪費を減らすのはもちろんだが、
日常生活についても書かれていた。
レンタルビデオ店に行かずNetflixで見ると移動時間が減るなど。
別にそこまで気にしすぎなくてもいいのではと感じたが。
レンタルビデオ店は今では自分も行かないが
書店は実際に足を運びたくなったりするし、
結局 amazonの方が安かったりしてamazonで買うこともある。
ゲームとかもそう。

12章 文書化
文書化はすべきだと思う。
ただし、細かすぎる文書化をすればいいわけではないので注意する。
アップデートが頻繁なサービスの文書化などはしょっちゅう見た目が変わったりするので

13章 自動化
この辺はすべき。
事務的な作業でもpythonとか使って効率化できるとこはしていけるといいなあと思う。