Emacs のキーボードマクロ

テキスト編集で同じ操作を繰り返すなら、Emacsキーボードマクロ が便利。

例えば、以下のような内容の各行をダブルクォートで囲むとすると、

f:id:tt195361:20150609085310p:plain

  1. C-x ( → キーボードマクロ定義開始。ステータス行に "Defining kbd macro..." と表示される。
  2. 繰り返す操作を入力。ここでは、
    1. " → ダブルクォート入力
    2. C-e → 行の最後に移動
    3. " → ダブルクォート入力
    4. C-n → 次の行に移動
    5. C-a → 行の最初に移動
  3. C-x ) → キーボードマクロ定義終了。ステータス行に "Keybord macro defined" と表示される。

f:id:tt195361:20150609085319p:plain

定義したキーボードマクロを実行するには C-x e を入力。繰り返し実行するには C-u (universal-argument) を使う。C-u 1 0 C-x e で 10 回実行してみるとこうなる。

f:id:tt195361:20150609085326p:plain

ドメイン駆動設計のユビキタス言語とモデル駆動設計

エリック・エヴァンスのドメイン駆動設計 の中心となる二つの概念のまとめ。

ユビキタス言語

  • プロジェクト関係者がモデルの記述に使用する共通言語。
  • この言語をコミュニケーションからコードまで利用する。

モデル駆動設計

  • 解決する問題の理解とソフトウェアの実装の両方に使えるモデルを作成する。
  • そのモデルをコードに反映させる。

マウスやキーボードでの範囲選択

マウスやキーボードで範囲を選択する方法。

  • マウスでドラッグする。

のが普通だが、ボタンを押したままマウスを動かすのは大変なので、

  • シフトキーを押しながら、
    • 範囲の終わりをマウスでクリック
    • 矢印キー・Homeキー・Endキーで範囲を拡げる

という方法もある。選択されている範囲の修正もできる。

メモ帳などでの文書編集だけでなく、エクスプローラやエクセルなどでも同様です。

f:id:tt195361:20150605132819p:plain

Enum ではなく Class を使う

Enum を使うと switch ... case ... することになる。

たとえば、プログラム言語を Enum で定義して、その名前を表示する例。

using System;

namespace UseClassInsteadOfEnum
{
    public enum ProgrammingLanguageEnum
    {
        CSharp,
        Java,
    }
    
    public class ByEnum
    {
        public static void PrintName(ProgrammingLanguageEnum progLang)
        {
            switch (progLang)
            {
                case ProgrammingLanguageEnum.CSharp:
                    Console.WriteLine("C#");
                    break;
                case ProgrammingLanguageEnum.Java:
                    Console.WriteLine("Java");
                    break;
                default:
                    throw new ArgumentException("progLang");
            }
        }
    }
}

Enum だと、

  • なにかするには switch ... case ... を書かないと、、、
  • 一応 default も書いておかないと心配だし、、、
  • 将来 Enum のメンバを変更したら、全部調べて修正しないと、、、

と、悩むことになる。

クラスにすると、スッキリする。

using System;

namespace UseClassInsteadOfEnum
{
    public class ProgrammingLanguageClass
    {
        // 各項目を表わすインスタンスは static にする。
        private static ProgrammingLanguageClass _cSharp = new ProgrammingLanguageClass("C#");

        public static ProgrammingLanguageClass CSharp
        {
            get { return _cSharp; }
        }

        private static ProgrammingLanguageClass _java = new ProgrammingLanguageClass("Java");

        public static ProgrammingLanguageClass Java
        {
            get { return _java; }
        }

        private String _name;

        // コンストラクタは private にして、クラス外からインスタンスが作れないようにする。
        private ProgrammingLanguageClass(String name)
        {
            _name = name;
        }

        public String Name
        {
            get { return _name; }
        }
    }

    public class ByClass
    {
        public static void PrintName(ProgrammingLanguageClass progLang)
        {
            Console.WriteLine(progLang.Name);
        }
    }
}

使う側からは、同じように見える。

namespace UseClassInsteadOfEnum
{
    class Program
    {
        static void Main(string[] args)
        {
            ByEnum.PrintName(ProgrammingLanguageEnum.CSharp);
            ByClass.PrintName(ProgrammingLanguageClass.CSharp);
        }
    }
}

Visual Studio のスニペット

スニペットを使えば、決まった内容を簡単に入力できる。

たとえば、C# のプロパティを作成する場合、"propfull" と入力し、

f:id:tt195361:20150603132233p:plain

TAB キーを押すと、

f:id:tt195361:20150603132310p:plain

こんなふうに展開してくれる。あとは、反転表示されている部分を編集する。

  • TAB キーで次の反転表示に移動
  • Enter キーでスニペット編集終了

よく使うのは、"if", "for", "foreach", "try", "tryf" など。

"Tools -> Code Snippets Manager..." で、どんなスニペットがあるか確認できる。

f:id:tt195361:20150603133225p:plain

単体テスト用のものもあるとは、知りませんでした。

プログラムをオブジェクト指向にするには

プログラムをオブジェクト指向にするには、

  • そのプログラムがなにをするか、に加えて、
  • それをどこにやらせるか、

が重要になる。

気を付けるのは、他のクラスのプロパティを使って、いろいろ計算しているところ。次の例では、適用期間 (ApplicableTerm) クラスを使うクラスが、ApplicableTerm の Start と End プロパティから期間内かどうかを求めている。

using System;

namespace ObjectOrientedBadSample
{
    public class ApplicableTerm
    {
        private TimeSpan _start;
        private TimeSpan _end;

        public TimeSpan Start
        {
            get { return _start; }
        }

        public TimeSpan End
        {
            get { return _end; }
        }
    }

    public class ApplicableTermUser
    {
        public void BadUser(ApplicableTerm term)
        {
            TimeSpan someTimePoint = new TimeSpan();    // ある時点
            if (term.Start <= someTimePoint && someTimePoint <= term.End)
            //  ========================================================
            {
                // 適用期間内
            }
        }
    }
}

他のクラスのプロパティを使って自分で計算するのではなく、そのプロパティを提供するクラスにやってもらう。

using System;

namespace ObjectOrientedBetterSample
{
    public class ApplicableTerm
    {
        private TimeSpan _start;
        private TimeSpan _end;

        public bool IsIncluded(TimeSpan timePoint)
        {
            return _start <= timePoint && timePoint <= _end;
        }
    }

    public class ApplicableTermUser
    {
        public void GoodUser(ApplicableTerm term)
        {
            TimeSpan someTimePoint = new TimeSpan();    // ある時点
            if (term.IsIncluded(someTimePoint))
            //  ==============================
            {
                // 適用期間内
            }
        }
    }
}

知識レベル-操作レベルを使うとき(1)

アナリシスパターンの知識レベル-操作レベルを使うときとして、

  • プログラムの再コンパイルなしで、
  • なにかの種類を増減したい。

場合がある。

たとえば、測定の種類として身長や体重などがあるとする。これらがプログラムのコンパイル時点で決定できるならば、以下のように enum で表現できる。

namespace KnowledgeOperation1
{
    public enum MeasurementTypeEnum
    {
        Height,
        Weight,
        // . . . . .
    }

    public class WrittenInSourceCode
    {
        MeasurementTypeEnum _measType;
    }
}

測定の種類が増減し、プログラムを再コンパイルせずに対応するには、

  • 測定の種類をクラスにして、
  • そのインスタンスを生成・破棄する

ようにしておけばよい。

namespace KnowledgeOperation1
{
    // new MeasurementTypeClass("Height");
    // new MeasurementTypeClass("Weight");
    // . . . . .

    public class MeasurementTypeClass
    {
        public MeasurementTypeClass(string name)
        {
            //
        }
    }

    public class UsingReference
    {
        MeasurementTypeClass _measType;
    }
}

参考: アナリシスパターンを読もう