2012年4月18日水曜日

DisplayNameを表示するHtmlHelperの拡張メソッド

たぶん誰もが思いつくであろう、DisplayName属性の値を表示するHtmlHelperの拡張メソッドです。

どうして標準でないんだろう。あれ、知らないだけなのか...?


2012年4月14日土曜日

ASP.NET MVC ひとり反省会

最近、ちょこちょこASP.NET MVC(主に2)の開発を経験して、失敗とそれに対する自分なりの正解を覚え書きします。

今まで試行錯誤とWebで先輩方の記事を読んだり、ソースコードを読んだりして、頭の中だけでやっていたんですが、やっぱり少しづつでも文章にして、整理していかないといけないと思ったので。



反省と対策


まずURL、URLをしっかり考える

ちゃんとURLを設計してから、組み立てる。


なぜ

しっかりしたURL設計がないと、ルーティングやコントローラの構成が破綻してしまいがち。
あとでこれを調整するのは辛い作業だった。


どうする

それぞれの機能をちゃんと考えて、相応しいURLを設計する。全てはそれから。


ビューのモデル(ビューモデル)は、ビュー毎に作成する

似た構成のビューのモデルは、つい共用してしまいがちだけど...

なぜ

片方に仕様変更が入った時、もう片方にも影響が出て大変。
この歪を下手に吸収しようとすると、どんどんワケのわからない状態になって、分けておいたほうがよほど楽だったということになってしまった。


どうする

最初からビューごとにビューモデルを分けて作っていく。


追記(2012/04/17)

@onosさんにご指摘頂いたので追記です。
単にビューごとにビューモデルを分けて作るのではなく、どこを切り離し、どこを共通化するのか、しっかり設計する必要がある。

例えば、検証周りの設定は単に分割して作ってしまうと、同じ設定を多方でバラバラに設定することになってしまう可能性がある。
そういった共通化と分離のバランスを取っていく。



クライアント側の検証と、サーバ側(ドメインモデル)の検証は別に考える

検証のやり方はともかく、これは混同しないほうがいいと思った。


なぜ

そもそも、クライアント側とサーバ側とでは、検証の目的が違うはず。
クライアント側は、ユーザビリティの向上が主な目的。
サーバ側は、データを守ることが主な目的。


どうする

検証の設定を無理に共有させようとしない。
それぞれのコンテキストに必要十分な検証を別々に設定する。また、各検証の目的を曖昧にしない。


ドメインのエンティティは、コントローラやビューに露出させない


なぜ

エンティティの制御はモデル内で完結しないと、何が起こるのかわからなくなってしまう。
また、ビューでエンティティを使っていると、エンティティがビューに依存していってしまう。
そうして、ビジネスロジックとは関係ないモノがモデル内で増殖してしまった。


どうする

モデルのアウトラインとなる、サービス層のインターフェイスにエンティティを使用しない。
DTO的なものを、それぞれのコンテキスト別に用意し、それにエンティティのデータを移して使う。
AutoMapperはとても便利。


まとめ

ふと書いておこうと思いついて、パッとでてくるものだけ殴り書きです。
こうやってベストプラクティス的なものを構築していくのは楽しい。

2011年12月3日土曜日

[C#]CompareToの実装を楽にする


最近ちまちまとIComparableを実装していた。例えばこんなクラスがあったら、

public class Version
{
    public Version()
    {
    }
    
    public Version(int major, int minor, int build, int revision)
    {
        Major = major;
        Minor = minor;
        Build = build;
        Revision = revision;
    }
 
    public int Major { get; set; }
    public int Minor { get; set; }
    public int Build { get; set; }
    public int Revision { get; set; }
}

こんな風にIComparableを実装していく。

public class Version : IComparable, IComparable<Version>
{
    public Version()
    {
    }
 
    public Version(int major, int minor, int build, int revision)
    {
        Major = major;
        Minor = minor;
        Build = build;
        Revision = revision;
    }
 
    public int Major { get; set; }
    public int Minor { get; set; }
    public int Build { get; set; }
    public int Revision { get; set; }
 
    public int CompareTo(object obj)
    {
        return CompareTo(obj as Version);
    }
 
    public int CompareTo(T other)
    {
        if (other == null)
            return 1;

        int majorCompared = Major.CompareTo(other.Major);
        if (majorCompared != 0)
            return majorCompared;

        int minorCompared = Minor.CompareTo(other.Minor);
        if (minorCompared != 0)
            return minorCompared;

        int buildCompared = Build.CompareTo(other.Build);
        if (buildCompared != 0)
            return buildCompared;

        return Revision.CompareTo(other.Revison);
    }
}

3つぐらい実装したところで、面倒すぎて死ぬかと思った。しかも、比較対象が参照型だと、nullの判定まで必要でさらに面倒くさい。

そこで、ちょっと考えることにした。

どうしよう


どうせほとんどのクラスが、内部フィールドの単純な比較の合成なんだから、宣言的にそういうことをやってくれる汎用クラスを一つ作れば良い。

具体的には以下のような使い方をしたい。


[TestFixture]
public class ComplexComparerTest
{
    [Test]
    public void Comparer_Test()
    {
        var comparer = new ComplexComparer<Version>();
        comparer
            .Member(v => v.Major)
            .Member(v => v.Minor)
            .Member(v => v.Build)
            .Member(v => v.Revision)
            ;
  
        var ver_1_0_0_0 = new Version(1, 0, 0, 0);
        var ver_2_0_0_0 = new Version(2, 0, 0, 0);

        Assert.IsTrue(0 < comparer.Compare(ver_1_0_0_0, ver_2_0_0_0));
        Assert.IsTrue(0 > comparer.Compare(ver_2_0_0_0, ver_1_0_0_0));
    }
}

ラムダ式で比較に利用するメンバを設定するメソッド(Memberメソッド)を用意し、設定した順に比較が行われるようにしたい。


実装しよう

というわけで実装する。適当にメンバを用意する。


public class ComplexComparer<T> : IComparer, IComparer<T>
{
    public ComplexComparer<T> Member<TMember>(Expression<Func<T, TMember>> expression)
        where TMember : IComparable
    {
        // ここ実装
        return this;
    }

    public int Compare(object x, object y)
    {
        return Compare((T)x, (T)y);
    }

    public int Compare(T x, T y)
    {
        // ここ実装
        return 0;
    }
}

比較処理を順番に実行していけばいいんだから、比較デリゲートのリストを持つようにして、CompareToの中身を実装してしまおう。


public class ComplexComparer<T> : IComparer, IComparer<T>
{
    private readonly IList<Comparison<T>> _comparisons = new List<Comparison<T>>();

    public ComplexComparer<T> Member<TMember>(Expression<Func<T, TMember>> expression)
        where TMember : IComparable
    {
        // あとで実装
        return this;
    }

    public int Compare(object x, object y)
    {
        return Compare((T)x, (T)y);
    }

    public int Compare(T x, T y)
    {
        foreach (var comparison in _comparisons)
        {
            var compared = comparison(x, y);
            if (compared != 0)
                return compared;
        }

        return 0;
    }
}

あとはMemberメソッドで、比較デリゲートが追加されるようにするだけだ。


public ComplexComparer<T> Member<TMember>(Expression<Func<T, TMember>> expression)
        where TMember : IComparable
    {
        var compiled = expression.Compile();

        _comparisons.Add((x, y) => 
        {
            T xMember = compiled(x);
            T yMember = compiled(y);
   
            if (xMember != null)
                return xMember.CompareTo(yMember);
   
            if (yMember != null)
                return yMember.CompareTo(xMember) * -1;
   
            return 0;
        });

        return this;
    }

Compareメソッドにnullの場合の判定も入れておこう。


public int Compare(T x, T y)
    {
        if (x == null || y == null)
        {
            if (x != null) return 1;
            if (y != null) return -1;
            return 0;
        }
  
        foreach (var comparison in _comparisons)
        {
            var compared = comparison(x, y);
            if (compared != 0)
                return compared;
        }

        return 0;
    }



かんせい


public class ComplexComparer<T> : IComparer, IComparer<T>
{
    private readonly IList<Comparison<T>> _comparisons = new List<Comparison<T>>();

    public ComplexComparer<T> Member<TMember>(Expression<Func<T, TMember>> expression)
        where TMember : IComparable
    {
        var compiled = expression.Compile();

        _comparisons.Add((x, y) => 
        {
            T xMember = compiled(x);
            T yMember = compiled(y);
   
            if (xMember != null)
                return xMember.CompareTo(yMember);
   
            if (yMember != null)
                return yMember.CompareTo(xMember) * -1;
   
            return 0;
        });
        
        return this;
    }

    public int Compare(object x, object y)
    {
        return Compare((T)x, (T)y);
    }

    public int Compare(T x, T y)
    {
        if (x == null || y == null)
        {
            if (x != null) return 1;
            if (y != null) return -1;
            return 0;
        }
  
        foreach (var comparison in _comparisons)
        {
            var compared = comparison(x, y);
            if (compared != 0)
                return compared;
        }

        return 0;
    }
}

使ってみる

Versionクラスに実装してみる。

public class Version : IComparable, IComparable<Version>
{
    public Version()
    {
    }
 
    public Version(int major, int minor, int build, int revision)
    {
        Major = major;
        Minor = minor;
        Build = build;
        Revision = revision;
    }
 
    public int Major { get; set; }
    public int Minor { get; set; }
    public int Build { get; set; }
    public int Revision { get; set; }
 
    private static readonly ComplexComparer<Version> _comparer = 
        new ComplexComparer<Version>()
            .Member(v => v.Major)
            .Member(v => v.Minor)
            .Member(v => v.Build)
            .Member(v => v.Revision);
 
    public int CompareTo(object obj)
    {
        return CompareTo(obj as Version);
    }
 
    public int CompareTo(T other)
    {
        return _comparer.Compare(this, other);
    }
}

とてもすっきりしたし楽になった。これなら間違いも少なくなるだろう。めでたしめでたし。

まとめ


  • 内部の比較デリゲートに詰める処理をアレンジすればいろいろバリエーションが広がる。
  • Expressionは便利
  • 宣言的な書き方は楽だし間違いが少ない。

2011年9月26日月曜日

[映画]「ワイルド・スピード MEGA MAX」の感想

シリーズ通してアクションもストーリも無茶なところが魅力のワイルド・スピードですが、今回はもっと無茶苦茶だった。

車は吹っ飛びまくるし爆発しまくる。アクションの迫力は最高。

でもちょっと気になるのはストーリで、基本的に主人公たちは強盗団のとんでもない悪党なので、なんかシリアスなシーンになっても、感情移入できない。
その辺り、もっと何も考えずにド派手なカーアクションをスカッと観れるようになっていたほうが気持よく鑑賞できる気がする。

あと、筋肉ムキムキのマッチョマン二人が、暑苦しい殴り合いをするシーンがなかなか。

2011年9月19日月曜日

[AVA]SA58 Para カスタムめも


  • ロングレンジバレル
  • 人体工学グリップ
  • リコイルコントロールストック
ある程度連射可能な反動、集弾性で、今のところ一番扱い易い。

Paraの苦手な中距離以降の交戦でも、初弾の精度と、集弾性で戦える。また、エイムモードも具合がよくて、集弾性が向上するので使える。

[映画]「世界侵略: ロサンゼルス決戦」の感想


  • 2時間ずっと銃撃戦
  • 退却NO! 2-5! (NO RETREAT! 2-5!)
  • ミシェル・ロドリゲス いいですわね

最近流行りの?エイリアンが突然攻めてくる系の本作。
他の作品と違う点は、ひたすら現場目線で展開されるところだった。
この手の映画でありがちな、ペンタゴンとか研究機関で指揮官とか学者が出てくるシーンが無い。
ほとんど主人公の海兵隊の部隊の目線で展開され、未知の敵と死闘を繰り広げる。

あと、エイリアンが意外と強くない(強いけど)。海兵隊員のM4でも、撃ちまくると、なかなか倒れないけど倒せるし、火力もそれほど激烈ではない。
また、エイリアンもこちらの軍隊のような、組織・軍事的行動を取る。物陰に隠れながら進んできたり、味方をカバーしたり、海兵隊の装甲車には、重火器を持ちだして対抗するなど、人間の軍隊のような様子を見せるので、さながら戦争映画のような迫力ある戦闘になっていた。

戦闘シーン以外では、主人公となる海兵隊の小隊の中に最初にあった軋轢やわだかまりが、戦闘を通して徐々に解消されていく描画がうまくて、演出に花を添える。

海兵隊の宣伝映画かと思うほど、海兵隊がかっこよくて大活躍だが、空軍兵士として登場するミシェル・ロドリゲスもかっこいい。この人はほんとに女性兵士の役が似合うなあ。

[映画]「RED」の感想

  • テンポの良い コメディ・アクション
  • ブルース・ウィリス 強すぎ
引退した凄腕のおじいちゃん・おばあちゃんが暴れる話。ブルース・ウィリスは歳をとっても超人的な強さ。

特攻野郎Aチームみたいなハチャメチャ・アクションとか、ナイト・アンド・デイのような巻き込まれ型ロマンスとか。

主人公(ブルース・ウィリス)が、スピンするパトカーからスッと降りて、そのままハンドガンで反撃するシーンがすごかった。

Factorio: Space Exploration クリア記録

 工場建設クラフトゲーム、Factorio の MOD である Space Exploration のクリア記録です。 はじめに プレイ時間は約 350 時間、2023年10月から2025年2月にかけて15ヶ月間に及びました。この期間中3人の友人と毎週末、工場勤務に明け暮れました...