だてブログ

ねことテクと趣味のブログ

プリンシプルオブプログラミング メモ

前提

プログラミングに特効薬はない

  • Why: ソフトウェアは本質的に困難性を持っている
  • How: 万能的な解決方法はない。地道に歴史を学び複雑さと戦う。

コードこそが設計書

コードは修正されるもの

  • 変更に強いコードを書くこと。
  • 書いてる時間よりも読む時間のほうが長い。
  • 常に読みやすいコードを書くこと

原則

KISS:コードをシンプルに保つ

  • ダメなコードは無秩序で複雑。
  • 構成する各要素がすべてシンプル、担う責務が最小限、関連もシンプル
  • プログラミング中、動作させるために最もシンプルなものは何か、常に考える
    1. NG: 新しいトリッキーなコードを書きたい。クレバーでありたい。
    2. NG: 将来必要になるかもしれないと、過剰なコード書いてしまう
    3. NG: 勝手に用件を判断し、余計なコードを加えてしまう
  • より少ないことは、より豊かなこと。

DRY:コードのコピペ厳禁

  • 定数リテラルが直接コードに埋め込んであること->重複
  • コードを抽象化する。関数化、モジュール化、データは定数化。
  • やむを得ないDRY違反もある。インピーダンス・ミスマッチ。

YAGNI:コードは必要最低限

PIE:コードの意図を伝える

  • コードは書きやすさよりも読みやすさを優先。重視。
  • コードはたくさんの人がたくさんの回数読む => 読む時間のほうが多い
  • 実行効率よりも読む効率のほうが優先される。読めれば実行効率は後から何とでもなる。

SLAP:コードの抽象化レベルを合わせる

  • 関数を抽象レベルに沿って多階層に分割していき、同じ関数に属するコードの抽象レベルをすべて統一する。
  • 抽象レベルがそろうと結果的にコードは優れた書籍のようになる。(パラグラフライティング)
  • (縦に連なった)各関数の呼びだし->目次、各関数の定義->本文
  • 関数を構造化する。自身より一段低いレベルの関数を呼び出す処理が中心となる。(複合関数)
    • 複合関数の中身は極力小さく。下の階層を呼び出す一行でもよい。
    • 複合関数の中では、様々な抽象レベルの関数を呼ばない、同じ階層のものだけ呼ぶようにする。
  • 手順:具体的な処理を書く作業と、抽象化レベルをそろえる作業は別々に。

OCP:拡張に対して開いている/修正に対して閉じている

  • コードのふるまいを拡張できて、拡張してもそのほかのコードは全く影響を受けない。
  • コードにインターフェースを用いる。

名前重要:コードの命名は最重要規則

  • ふさわしい名前が付けられないということは、その要素が果たすべき役割を理解していないのと同じ
  • プログラマ同士のコミュニケーションするための最大の場。最大限の配慮を。
  • 名前はコードを読む人へのユーザーインターフェース
  • 適切に命名された分かりやすい名前の関数は内部処理の詳細を隠蔽しつつ、責務を名前で通知
    • コードを読んでいるとき、関数名を見ただけでその先の処理の概要が把握できるので適宜読み飛ばせる
    • コードを書いているとき、その関数を使用する場面においては、名前に導かれ、目的や使用方法がすぐに理解できるので、呼び出すのが簡単。
  • 名前が悪いと、読むことにすべてのリソースが奪われ、取り組むべき本来の作業を妨げられる
  • 名前にはより多くの情報を詰め込むようにする。短いコメント。複数候補から選ぶのがよい。
  • 名前は誤解されることのないように気を付ける。(変数名は動詞_名詞で書かないなど。)
  • 名前は効果と目的を説明する。手段はいらない。名前は発音可能、検索可能なものにする。
  • 名前を自分自身でチェックしたい場合、処理を書く前にそのテストや呼び出しを書くようにする

思想

プログラミングセオリー ~プログラミングを導く価値観~

 価値1. コードはコミュニケーションの場である
 価値2. コードの複雑性は排除する
 価値3. コードの変更が容易である
 原則1. 変更の影響を抑える
 原則2. 重複の排除する
 原則3. データとその操作は近くに
 原則4. コードに一貫性を持たせる
 原則5. 宣言型でプログラミング
 原則6. 変更理由でグルーピング

アーキテクチャ根底技法抽象

 1. 抽象:概念的な「線引き」
 2. カプセル化:データとロジックをグルーピング
 3. 情報隠蔽:必要のないものは見せない
 4. パッケージ化:モジュールをグルーピング
 5. 関心の分離:関心ごとにコードを分離
 6. 充足性、完全性、プリミティブ性 - 表現が十分かつ完璧かつ純粋
 7. ポリシーと実装の分離
 8. インタフェースと実装の分離
 9. 参照の一転性:定義は一度きり
 10. 分割統治:大きな問題を小さく割る

非機能要件:「機能以外の機能」の観点

 1. 変更容易性:コードを容易に変更する能力
 2. 相互運用性:ほかのソフトウェアと会話する能力
 3. 効率性:リソースをうまく使う能力
 4. 信頼性:機能を維持する能力
 5. テスト容易性:効果的にテストする能力
 6. 再利用性:再利用「する」「される」能力

7つの設計原理:コード妥当性レビュー観点

 1. 単純原理:シンプルにこだわる
 2. 同型原理:形にこだわる
 3. 対象原理:形の対称性にこだわる
 4. 階層原理:階層にこだわる
 5. 線形原理:処理の流れは直線にこだわる
 6. 明証原理:ロジックの明確性にこだわる
 7. 安全性原理:安全性にこだわる

UNIX思想:UNIXの底流にある暗黙知

 1. モジュール化の原則:控えめなモジュールを作る
 2. 明確性の原則:コードを明確にする
 3. 組み立て部品の原則:フィルタにして組み立てる
 4. 分離の原則:メカニズムからポリシーを切り離す
 5. 単純性の原則:コードはシンプルにする
 6. 倹約の原則:大きなコードは書かない
 7. 透明性の原則:ソフトウェア動作の「見える化
 8. 安定性の原則:ソフトウェアを安定させる
 9. 表現性の原則:情報はデータに寄せて表現
 10. 驚き最小の原則:予想通りのインタフェース
 11. 沈黙の原則:ソフトウェアは寡黙であれ
 12. 修復の原則:修復失敗時は処理停止
 13. 経済性の原則:プログラマの時間を大切に
 14. 生成の原則:「コードを書く」ためのコードを書く
 15. 最適化の原則:速いコードより正しいコード
 16. 多様性の原則:選択の多様性を受容する
 17. 拡張性の原則:拡張できる設計にしておく

UNIX哲学:UNIXを支え続けている哲学

 1. 小さいソフトウェアは美しい
 2. 一つのソフトウェアは一つの仕事
 3. できるだけ早くプロトタイプ着手
 4. 効率性より移植性を優先
 5. バイナリよりテキストファイル
 6. ソフトウェアのてこで力増幅
 7. シェルスクリプトで接着する
 8. 拘束的な対話ユーザーインターフェースは避ける
 9. ソフトウェアはフィルタとして設計
 ほか-環境カスタマイズ、軽薄短小カーネル、小文字使用、死人リン保護、沈黙は金、並列思考、部品コラボレーション、90% 解、劣るが勝る、階層指向

視点

凝集度:モジュールは「純粋」に

結合度:モジュール感は「疎遠」に

直交性:コードは独立させよ

可逆性:「UNDO」可能な選択をせよ

コードの臭い:コードの凶兆を見逃すな

技術的負債:問題コードは「借金」


習慣

プログラマは「怠慢」「短気」「傲慢」であれ

コードを掃除して帰る

「速い」コードより「良い」コード

エゴレスプログラミング - 仲間を頼る

ステップ・バイ・ステップ

ツールの多様性は善


手法


法則


京都一人旅

今回は卒業旅行に行けなかったので、代わりに京都に一人旅したことを書きます。

京都を選んだのは、すでに京都自体は修学旅行と家族旅行で二回行っているので旅行のハードルが低いためと、 そのくせに本当にずっといきたかったところに行けていなかったためです。 あと前々から桜がきれいだろうなぁと思っていたので、そうだ、京都へ行こうと一週間で決めました。


1日目は行けていなかったところを回りました。

f:id:Windnap:20180331150719j:plainf:id:Windnap:20180331150737j:plain
f:id:Windnap:20180331150701j:plainf:id:Windnap:20180331152420j:plain
渡月橋
f:id:Windnap:20180331152947j:plainf:id:Windnap:20180331152937j:plainf:id:Windnap:20180331152932j:plain
嵯峨野
f:id:Windnap:20180331153228j:plainf:id:Windnap:20180331153416j:plainf:id:Windnap:20180331153213j:plain
f:id:Windnap:20180331153222j:plainf:id:Windnap:20180331153335j:plain
鞍馬山鞍馬寺

2日目は京都市内の行ったことのない寺と神社をぶらぶらしました。

f:id:Windnap:20180331154328j:plainf:id:Windnap:20180331154337j:plain
龍安寺
f:id:Windnap:20180331154840j:plainf:id:Windnap:20180331154803j:plainf:id:Windnap:20180331154759j:plain
伏見稲荷大社

f:id:Windnap:20180331155123j:plainf:id:Windnap:20180331155134j:plainf:id:Windnap:20180331155732j:plain
f:id:Windnap:20180331155747j:plainf:id:Windnap:20180331155853j:plainf:id:Windnap:20180331155855j:plain
f:id:Windnap:20180331155915j:plainf:id:Windnap:20180331155928j:plainf:id:Windnap:20180331155934j:plain
f:id:Windnap:20180331155940j:plainf:id:Windnap:20180331160009j:plainf:id:Windnap:20180331160022j:plain

東寺五重塔木屋町通り夜桜、二条城ほか桜

今回3回目で、いまだ下鴨神社京都タワー北野天満宮など行っていないところがたくさんあるんですよね。。。 また行きたいです。ほんと。京都大好き。

PreferenceScreenをネストすると外のレイアウトが崩れる

PreferenceFragmentを使った設定画面で、例えば、複数画面のPreferenceを簡単に作ろうとすると

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceScreen android:title="test1">
        <Preference android:title="test1-1">
        <Preference android:title="test1-2">
    </PreferenceScreen>
</PreferenceScreen>

とネストをかければ複数画面を作れますが、これだとネストされたPreferenceScreen画面には、親のPreferenceScreenでデザインしたであろうレイアウト(ActionBarやbuttombarなど)が適用されません。 そこで、親のPreference要素をクリックすると別のフラグメントに置き換えるという、従来のFragmentの切り替え方でなんとかしました。

SettingActivity.java

public class SettingActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // activity_setting.xmlはfragmentを入れるレイアウトxml
        setContentView(R.layout.activity_setting);
        getFragmentManager().beginTransaction().replace(R.id.fragment_container,
                new UserPreferenceFragment()).commit();
    }
}

class UserPreferenceFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // preference_header.xmlは親の設定画面xml
        addPreferencesFromResource(R.xml.preference_header);
        Preference.OnPreferenceClickListener subpreference = new Preference.OnPreferenceClickListener() {
            @Override
            public boolean onPreferenceClick(Preference preference) {
                getPreferenceScreen().removeAll();
                switch(preference.getKey()) {
                    // preference_test1,2,3 は子の設定画面
                    case "test1":
                        addPreferencesFromResource(R.xml.preference_test1); break; 
                    case "test2":
                        addPreferencesFromResource(R.xml.preference_test2); break;
                    case "test3":
                        addPreferencesFromResource(R.xml.preference_test3); break;
                }
                return true;
            }
        };
        final Preference test1 = (Preference) findPreference("test1");
        final Preference test2 = (Preference) findPreference("tesst2");
        final Preference test3 = (Preference) findPreference("test3");
        test1.setOnPreferenceClickListener(subpreference);
        test2.setOnPreferenceClickListener(subpreference);
        test3.setOnPreferenceClickListener(subpreference);
    }
}

ホント地味にですが、setOnPreferenceClickListenerをできるだけきれいに、複数作るのに苦労しました。(ひとつずつ定義して当てはめるには量が多かったので)

参考:

f:id:Windnap:20170730180705j:plain