2015年5月27日水曜日

コマンド実行型のコントローラ実装例

backbone.jsの影響を受けてから2年。
新しい技術を追い求めて色々と勉強してきて辿り着いた答えは

 今も昔もバグの無いソフトを作る事。

これに尽きる。

オブジェクト思考だとか関数型だとかトレンドに追随してきてコーディングスタイルの改変して実装している段階は使い物にならない。
すべては枯れた技術で実装される必要があるからだ。

師匠レベルと思っている人の業を見ると10年以上前のソースをそのまま使いまわせるんだから、そろそろ自分も落ち着こうと思っている。



まず第1段としてオレオレデザインパターンガイドのMVC部分から書いてみた。


Viewはどんな書き方しても大丈夫だから省略。


Modelクラス→Collectionクラスへとデータを集約していく。

複数のCollectionクラスから特定のデータを寄せ集めて作る人がFactory。
生成物はProductModel(コンポジットクラス)

これをController(ストラテジークラス)で被せる。

new Controller(Model);  // こんな具合

Controllerには関数テーブルもどきにシナリオが書けるように実装している。
こうすることでドキュメント{ユースケース図とシナリオ}と1対1で一致するようになって
テンプレートクラスの自動生成が可能になる期待ができるわけである。
もしこれが通信部分ならシーケンス図に該当する。

サブプロセス[箇条書きのシナリオを実装]
プロセス[サブプロセスのコンテナ]、コマンドを受けて該当するサブプロセスをピックアップして実行するだけ。
実行処理はサブプロセスワーカーという担当者で、処理するサブプロセスとパラメータを渡して後は頼んだという感じに任せる。


----Model部(Modelクラスがテーブル定義でCollectionクラスがレコード)----
class Model
{
    public int param1 { get; set; }
    public int param2 { get; set; }
}

class Collection : List
{
}

// Compisiteクラス()
class ProductModel
{
    Model model1;
    Model model2;
}

// FactoryクラスでProductModelを生成
class Factory
{
    Collection col1 = new Collection();
    Collection col2 = new Collection();

    public ProductModel Create()
    {
        return new ProductModel(col1[0], col2[1]); // 発展形はControllerクラスに被せて戻り値で返す
    }
}
----------------------------------------------------

----Controller部(backbone.jsではRouterに当たる部分)----


using System;
using System.Collections.Generic;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        //コントローラ生成
        var ctrl = new testController(); //今回はModelなし
        //試しに非同期Start・同期Executeの2種類実行
        ctrl.Start(Instruction.TEST1, null).Warker.Wait();
        ctrl.Execute(Instruction.TEST2, new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase) { { "foo", "bar"}, { "hello", "world" } });
        Console.ReadKey();
    }
}

/// <summary>
/// サンプル実装コントローラ
/// </summary>
public class testController
{
    private MainProc thread;
    public testController()
    {
        this.thread = new MainProc()
        {
            { Instruction.TEST1, new SubProc(test1, "TEST1") },
            { Instruction.TEST2, new SubProc()
                {
                    { StepID.Step1, new SubFunc(test21, "T2-1", StepID.Step2, StepID.FIN) },
                    { StepID.Step2, new SubFunc(test22, "T2-2", StepID.Step3, StepID.FIN) },
                    { StepID.Step3, new SubFunc(test23, "T2-3", StepID.Step4, StepID.FIN) },
                    { StepID.Step4, new SubFunc(test24, "T2-4", StepID.FIN, StepID.FIN) },
                }
            },
        };
        this.thread.Initialize();
    }

    public SubProcWarker Start(Instruction inst, Dictionary<string, string> arguments = null)
    {
        return this.thread.Start(inst, arguments);
    }

    public bool Execute(Instruction inst, Dictionary<string, string> arguments = null)
    {
        var proc = Start(inst, arguments);
        proc.Warker.Wait();
        return !proc.IsAbort;
    }

    bool test1()
    {
        Console.WriteLine("TEST1: OK");
        return true;
    }

    bool test21(SubProcWarker sender)
    {
        var value = sender.Arguments["Foo"];
        Console.WriteLine("TEST2-1: " + value);
        return true;
    }
    
    bool test22(SubProcWarker sender)
    {
        var value = sender.Arguments["hello"];
        Console.WriteLine("TEST2-2: " + value);
        return true;
    }
    bool test23(SubProcWarker sender)
    {
        var value = sender.ExeFunc.StateMessage;
        Console.WriteLine("TEST2-3: " + value);
        return true;
    }

    bool test24()
    {
        Console.WriteLine("TEST2-4: OK");
        return true;
    }
}

/// <summary>
/// サブプロセス用ステップ番号
/// </summary>
public enum StepID
{
    FIN = 0,
    Step1,
    Step2,
    Step3,
    Step4,
    Step5,
}
public class StepIDComparerIgnoreCase : IEqualityComparer<StepID>
{
    public bool Equals(StepID x, StepID y)
    {
        return x.Equals(y);
    }

    public int GetHashCode(StepID obj)
    {
        return (int)obj;
    }
}
/// <summary>
/// コマンド一覧
/// </summary>
public enum Instruction
{
    TEST1,
    TEST2,
}
public class InstructionComparerIgnoreCase : IEqualityComparer<Instruction>
{
    public bool Equals(Instruction x, Instruction y)
    {
        return x.Equals(y);
    }

    public int GetHashCode(Instruction obj)
    {
        return (int)obj;
    }
}


/// <summary>
/// ファンクション(1処理単位)
/// </summary>
public class SubFunc
{
    private IFunc function;
    private StepID TRUE;
    private StepID FALSE;
    public string StateMessage { get; private set; }
    private SubFunc FuncT;
    private SubFunc FuncF;
    public SubFunc(Func<bool> action, string stateMessage, StepID TRUE, StepID FALSE)
    {
        this.function = new FuncA(action);
        this.TRUE = TRUE;
        this.FALSE = FALSE;
    }
    public SubFunc(Func<SubProcWarker, bool> action, string stateMessage, StepID TRUE, StepID FALSE)
    {
        this.function = new FuncB(action);
        this.TRUE = TRUE;
        this.FALSE = FALSE;
        this.StateMessage = stateMessage;
    }
    public void Initialize(SubProc funcTable)
    {
        this.FuncT = (this.TRUE == StepID.FIN) ? null : funcTable[this.TRUE];
        this.FuncF = (this.FALSE == StepID.FIN) ? null : funcTable[this.FALSE];
    }
    public SubFunc Dispatch(SubProcWarker sender)
    {
        return this.function.Dispatch(sender) ? this.FuncT : this.FuncF;
    }

    #region "内部I/F"
    interface IFunc
    {
        bool Dispatch(SubProcWarker sender);
    }
    class FuncA : IFunc
    {
        private Func<bool> action;
        public FuncA(Func<bool> action)
        {
            this.action = action;
        }
        public bool Dispatch(SubProcWarker sender)
        {
            return this.action();
        }
    }
    class FuncB : IFunc
    {
        private Func<SubProcWarker, bool> action;
        public FuncB(Func<SubProcWarker, bool> action)
        {
            this.action = action;
        }
        public bool Dispatch(SubProcWarker sender)
        {
            return this.action(sender);
        }
    }
    #endregion
}

/// <summary>
/// ファンクションテーブル(1プロセス単位)
/// 本テーブル内で1アクションの処理が完結する
/// </summary>
public class SubProc : Dictionary<StepID, SubFunc>
{
    public SubProc()
        : base(new StepIDComparerIgnoreCase())
    {
    }
    public SubProc(Func<bool> action, string stateMessage)
        : base(new StepIDComparerIgnoreCase())
    {
        this[StepID.Step1] = new SubFunc(action, stateMessage, StepID.FIN, StepID.FIN);
    }
    public SubProc(Func<SubProcWarker, bool> action, string stateMessage)
        : base(new StepIDComparerIgnoreCase())
    {
        this[StepID.Step1] = new SubFunc(action, stateMessage, StepID.FIN, StepID.FIN);
    }
    /// <summary>
    /// 初期化処理
    /// </summary>
    public void Initialize()
    {
        foreach (var model in this.Values)
        {
            model.Initialize(this);
        }
    }

    public SubProcWarker Start(Dictionary<string, string> arguments = null)
    {
        return new SubProcWarker(this[StepID.Step1], arguments);
    }
}

/// <summary>
/// サブプロセス実行クラス
/// </summary>
public class SubProcWarker
{
    public Task Warker { get; private set; }
    public Dictionary<string, string> Arguments { get; private set; }
    public SubFunc ExeFunc;
    public bool IsAbort { get; set; }
    public bool IsPause { get; set; }
    public SubProcWarker(SubFunc func, Dictionary<string, string> arguments = null)
    {
        this.Arguments = arguments;
        this.ExeFunc = func;
        this.Warker = Task.Run((Action)MainLoop);
    }

    private void MainLoop()
    {
        while (null != this.ExeFunc)
        {
            if (this.IsAbort) break;
            if (!this.IsPause)
            {
                this.ExeFunc = this.ExeFunc.Dispatch(this);
            }
            System.Threading.Thread.Sleep(1);
        }
    }
}

/// <summary>
/// プロセス(コマンド一覧、複数のサブプロセスを抱える)
/// </summary>
public class MainProc : Dictionary<Instruction, SubProc>
{
    public MainProc()
        : base(new InstructionComparerIgnoreCase())
    {
    }
    public void Initialize()
    {
        foreach (var funcTable in this.Values)
        {
            funcTable.Initialize();
        }
    }

    public SubProcWarker Start(Instruction inst, Dictionary<string, string> arguments)
    {
        return this[inst].Start(arguments);
    }
}

Dictionary型で大小区別なくKeyで値を取得する方法

Dictionary型で大小区別せずKey値を設定したい場合、以下のやり方でできます。


using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        var a = new dic1()
        {
            { "aaa", "123" },
        };
        Console.WriteLine(a["AAA"]);
        Console.ReadKey();
    }
}

class dic1 : Dictionary<string, string>
{
    public dic1()
        : base(StringComparer.InvariantCultureIgnoreCase)
    {
    }
}
enum foo
{
    aa = 1,
    bb,
}
class dic2 : Dictionary<foo, string>
{
    public dic2()
        : base(new fooComparerIgnoreCase())
    {
    }
}

class fooComparerIgnoreCase : IEqualityComparer<foo>
{
    public bool Equals(foo x, foo y)
    {
        return x.Equals(y);
    }

    public int GetHashCode(foo obj)
    {
        return (int)obj;
    }
}

2015年5月7日木曜日

ダイエット方法のまとめ

Pulsenseを使い始めて1週間でわかったこと。

今回はダイエット方法の総集編です。


・体重の変化は3日前の食生活&運動結果の現れ
 この情報はどこみてもないし、栄養の吸収の1日で行われることからなぜ3日前なの?と聞かれると根拠を説明できませんが、実体験から得られた結果なので参考程度にみてください。
 例えば、昨日沢山トレーニングしたからすぐに結果になって現れる事はなくて、
 実際1日に乗って変化した数字には図る前に飲食したものも含まれるから、
 主に1日の変化は体内の水分量なんだろうと見てる。
 2時間ジョギングすれば1.5kgも減少したりするし、
 測る前に500mlの飲み物を飲んでいれば500g増えるんだろうからもう少し長い目で変化を見る必要があるのは違いないだろう。



・規則正しい食生活(1日3食)を守る事。
 ざっくり1日の摂取カロリー計算すると
  朝:300kcal
  昼:1000kcal
  夜:800kcal
  1日:2100kcal

 普通にいつもどおりの食生活に間食、居酒屋等に行いったりすると、そこで食べた分すべてが体重増加になる。
 居酒屋にいけば+500kCal、
 間食すれば+500kCal

 これがどれくらいの影響があるかというと
 7000kCal=1kgなので、
 7kCal= 1g
  500kcal=72g
 1000kcal=144g 

 居酒屋にいけば+0.1kg増える、間食もすれば+0.1kg増える。
 両方なら+0.2kg。これを節制無しに週1ペースでやっていれば1ヶ月で+1kg増加

 体重を増やしたくないなら野菜中心の食事がベストになる。
 野菜といっても食べるなら葉っぱの部分。
 
 例えばキャベツ1玉で30kCalなので、いくら食べても大丈夫です。

 実は栄養が豊富なので、栄養バランスの調整にはいいけど、
 カロリーセーブするなら葉っぱがオススメです。

 ちなみにお腹が鳴るレベル=-500kcalくらい

 お腹がなって補給するご飯を食べる量は
 ご飯1.5杯分くらい(300gくらい?)をキープ
 
 これで24時間空腹感を感じる状態になってダイエットには調度良いと思います。

 もちろん空腹に感じて食べるものは栄養バランスを考えて沢山の種類を食べる事ですよ。


・減量トレーニング
 運動の結果を心拍数で計算するととても分かりやすいのでオススメ。

 性別、個人差、年齢とともに新陳代謝は下がっていくので全てに当てはまらないが
 1日の新陳代謝2000kCal/24時間
 1時間あたり=83kcal(心拍60)
 
 運動して心拍120で1時間運動すると
 平常時の2倍新陳代謝が促進されるので
 +83kCalということになる。

 1日-500kcalを目指すなら、
 運動だと6時間必要になるので、
 ジムで2時間運動したから1食分(800kcal)食べてもいいかというとそうではない。
 2時間運動しても-160kCal分しかならないし、ご飯1杯分にも満たしてない事が分かる。
 


・筋肉強化
 筋肉は脂肪より重いので、体重は増えます。
 ここでポイントなのが、体重と体脂肪率の関係。
 体重が増えて体脂肪率が減ってるならダイエットは成功。
 運動を熱心にしているから筋肉がついてきている証拠なので、やり始めの時はこの傾向に近いデータが出てくる。
 体重↑、体脂肪率↑ ・・・要:食の改善。3日以内前に食べたものに油が多く含まれるものがあると思われる。
 体重↑、体脂肪率↓ ・・・運動強度が高くて筋肉が付いてきている。ずっと筋肉の成長はある程度経つと安定するので体重が増えても気にせず続けるのが良い。

 体重↓、体脂肪率↑ ・・・要:食の改善。しかもダイエットに失敗している。体重計に乗るタイミングを朝起きた時、寝る前などいつも同じ時間にすることで正しい変化を記録することをオススメします。

 体重↓、体脂肪率↓ ・・・安定した有酸素運動をしている。体脂肪がちゃくちゃくと燃焼している証拠。
 

まとめると、
 どこでも言われていることで

 食生活の改善と適度な運動。


これに尽きる。

あと、

 運動しても意外とカロリー消費はしない。

これを付け加えておこう。

 食生活>>運動


通販で痩せる健康食品とかあって飲むだけで痩せましたとか

個人の感想を放送してると思うけど、もし本当にそうなら、

その健康食品が1食分だと理解しておく事が必要。

つまり、
 
 普段の食事を健康食品で置き換える。


これくらいの意気込みで食べる量を抑える必要があることを思っておくこと。




上に書いた事は既にわかりきっている事だし、既にダイエット経験者で変化を感じないと思ってる人は、処方箋なしにやっているからなので効果が出てこない。
今まで書いてきた事を読めば数字を含めたより詳しい説明があるけど、それらをまとめると

食生活の改善なら
 ・量を減らしたくないなら野菜(葉っぱ)を増やす。
 ・朝食とかでも2回に分けて食べる。
 ・油や高カロリー食品、炭水化物(=糖分)の摂取を控える。
 ・お腹が鳴るまで食べない
 ・食べる量は1回300~400gまで
 ・夜は野菜だけにしておく。(食べるならフランス料理みたいに一口サイズで沢山の種類)

適度な運動なら、
 ・1時間の有酸素運動で83kcalくらいなので、ご飯1杯分多く食べたなと思ったら3時間の運動が必要。
 ・ジムに通っても大体1回1~2時間くらいしかしないので、ジム1回分の消費カロリーは-100kcal。
  毎日2ヶ月続ければ-1kg
  週1なら半年で-1kg

 運動しても意外とエネルギー消費がないと思って運動した方がより沢山動けるし、
 運動した後に沢山動いたからパスタ1皿分(1000kcal)食べても平気とか思わなくなるので効果的。


最後に、ダイエットするなら運動の基準がないとメリハリが付かないので、
 心拍数を目安に運動する、お腹が鳴る食生活、夜は少ない食事ですませる


そもそも太るっていうのは過食なんですよね。
これでダイエットはうまくいきます。


Androider