調べ物した結果

現役SEが仕事と直接関係ないことを調べた結果とか感想とか

勝手にリファクタリング~Captura①~


久々に人のソースをリファクタリングする。
①というのは1回で全然できなかったことを示す。続くかわからないけど。

LICENSE

LICENSEはMITです。リンクと著作者を張っておく。

Copyright (c) 2020 Mathew Sachin

github.com

作成者には敬意を払い。作成物を公開してくれていることに感謝して作業します。

環境

OS:Windows10
IDEMicrosoft Visual Studio Community 2017 ⇒ Microsoft Visual Studio Community 2019
※ビルドできなかったからバージョンアップした。

準備

Gitから拾ってくれば基本的に完了なのだけど、環境の更新が必要だったので残す。
久しく見ず知らずのソースみてないから大分フレームワーク等おいていかれてる感。案のじょう使っている2017ComunityだとFreameworkが追いついておらず。
ということで2019Communityをぶち込んで再度ビルドトライで無事完了。なぜかテストの一部が通らないようだけどこのまますすめる。
ちゃんと調べていないが、Fakesをつかっているのでそこの問題だと思う。

4.7.2 の適用、VisualStudioの更新

gyazo.com
C# 8.0以降を使っているようなのでさらにビルドできず。下ということで、VisualStudio 2019を拾ってくる。
dotnet.microsoft.com

対象の選定

毎度のことながら全部はできないので(する必要がないところも多数あり。)

保守容易性インデックス高すぎ問題。

さーてどこリファクタリングするかなーってなったときに、いつもコードメトリクスをつかっているのだけれど
gyazo.com

と軒並み高スコア。現状、保守面で言えばリファクタリングは不要なレベルに思える。
プロジェクトレベルでは大した差がないので、もう少し掘ってみる。
gyazo.com
なんとか50台のNamespaceを見つけたので、今回はこの辺りにしようと思う。
Drawメソッドがいい具合に複雑らしい。といっても49行。だから手の入れようがないかもしれないが。
とりあえず今回はここに決まり。

リファクタリング

さーということでそろそろリファクタしていきましょう。

テストがねぇんです

ブレイクはってテスト流してみるも止まらず。プロジェクトの数に対してテストが少ないからもしやとは思ったけど、
どうやら今回手を入れる場所のテストコードはない様子。
テストかかなきゃ。。。なんでこの辺で全体像を見ていこう

Doxygenに喰わした結果がこちら。
gyazo.com
被参照がない・・・そんなぁ・・・

原因はよくわからず。VisualStudioの参照たぐっても「え?おまちがくね?」みたいな経路になって理解が追い付かず。
厳しい。
ということで参照はあきらめてメソッドの中を見ていく。

public override void Draw(IEditableFrame Editor, Func<Point, Point> PointTransform = null)
        {
            var curPos = Args.Location;

            if (PointTransform != null)
                curPos = PointTransform(curPos);

            float clickRadius = _settings.Radius;

            var d = clickRadius * 2;

            var x = curPos.X - clickRadius;
            var y = curPos.Y - clickRadius;

            var color = GetClickCircleColor();

            Editor.FillEllipse(color, new RectangleF(x, y, d, d));

            var border = _settings.BorderThickness;

            if (border > 0)
            {
                x -= border / 2f;
                y -= border / 2f;
                d += border;

                var borderColor = _settings.BorderColor;

                Editor.DrawEllipse(borderColor, border, new RectangleF(x, y, d, d));
            }

            if (Args.Clicks > 1)
            {
                var font = Editor.GetFont("Arial", 15);
                Editor.DrawString(Args.Clicks.ToString(), font, Color.Black, new RectangleF(x + 10, y + 10, d, d));
            }

            base.Draw(Editor, PointTransform);
        }

うーん。Drawとはいったもののこいつ自身は組み立てがメインに見える。しかし書く。テストを書くのだ。

voidのテスト面倒。

何かしら引数が返ってくれるほうが嬉しい。なにせメソッド実行前後の期待する動作読めないからさぁ・・・
最終的にはBase.DrawでBaseクラスを実行するので、これはなかなかにつらい。どうかけばいいのだ。

とりあえずやってることを整理してコメントだけ入れよう。改行も問題ないはずだ。

        public override void Draw(IEditableFrame Editor, Func<Point, Point> PointTransform = null)
        {
            // 楕円を書く
            var curPos = Args.Location;
            if (PointTransform != null) curPos = PointTransform(curPos);
            float clickRadius = _settings.Radius;
            var d = clickRadius * 2;
            var x = curPos.X - clickRadius;
            var y = curPos.Y - clickRadius;
            var color = GetClickCircleColor();
            Editor.FillEllipse(color, new RectangleF(x, y, d, d));

            // 枠線いるなら書く
            var border = _settings.BorderThickness;
            if (border > 0)
            {
                x -= border / 2f;
                y -= border / 2f;
                d += border;
                var borderColor = _settings.BorderColor;
                Editor.DrawEllipse(borderColor, border, new RectangleF(x, y, d, d));
            }

            // 文字を書く
            if (Args.Clicks > 1)
            {
                var font = Editor.GetFont("Arial", 15);
                Editor.DrawString(Args.Clicks.ToString(), font, Color.Black, new RectangleF(x + 10, y + 10, d, d));
            }
           
            base.Draw(Editor, PointTransform);
        }

少し解像度が上がった気がする。
前半。ArgsのLocationに対して、何かしらフォーマットをかけるにしてもクラス名から考えてマウス位置にでも円を書くのではないかと思う。
後半。ダブルクリックだろうか。わからんが、Clicksの数が2以上なら文字描画。
最後。既定のDraw処理を呼ぶ。
ということで大きく3個に分けられそうではある。xydを使いまわしてるのがちょっといやらしさを感じる。たぶんここの読み込みが肝になると思う。
ぐぬぐぬしてきたけどだいぶ見えてきたので少しテストがかけそう。塊の細部を抽出していく
とりあえずざっくり書いて、Failされることを確認。

    [Collection(nameof(Tests))]
    public class MouseClickStepTests
    {
        /* TODO 
            // 楕円を書く
            var curPos = Args.Location;
            if (PointTransform != null) curPos = PointTransform(curPos);
            float clickRadius = _settings.Radius;
            var d = clickRadius * 2;
            var x = curPos.X - clickRadius;
            var y = curPos.Y - clickRadius;
            var color = GetClickCircleColor();
            Editor.FillEllipse(color, new RectangleF(x, y, d, d));
        */
        [Fact]
        public void aaa()
        {
            Assert.True(false, "");
        }            
    }

gyazo.com
テスト実行すると無事に?エラーということでモリモリテストがかける。

楕円を書くところ。のテストをかく

もう少し詳細に行くと
①ArgsのロケーションからPositionをとる。
②TransformがあるならTransする。
③_SettingsのRadiusをつかって、描画サイズを決める。
④GetClickCircleColor()の結果で円の色を決める。
⑤上の設定でレッツトライ。
の流れ。

対象クラスがPrivateであることに気付く。AssemlyInfoがあれば無理やり公開できるのだけれども。
www.nuits.jp
こちらの通りにやったらうまく公開できたのでこれでいいと思う。

とりあえずということで、FillEllipseで受け取った中身をみて動作を確認する。Moqつかえばよい気がする。というかMoqつかえ案件(記事かいてる最中にきづいた)
テストの内容はひとまずちょっとずつ・・・ということでざっと作ってみた。

        public class _SettingsとArgsのLocationで楕円を書く
        {
            [Fact]
            public void Radiusが10の時はWidthとHeightは20になる()
            {
                var obj = new MouseClickStep(null, null, null, null);
                var actual = new FakeEditor();
                obj.Draw(actual, null);

                Assert.Equal(20, actual.Width);
                Assert.Equal(20, actual.Height);
            }
        }

    public class FakeEditor : IEditableFrame
    {
        public List<object> FillEllipseArguments;
        public void FillEllipse(Color Color, RectangleF Rectangle)
        {
            this.FillEllipseArguments = new List<object> { Color, Rectangle };
        }

んで、ざっと流すとこの調子でエラーをつぶしていけば後はごりっとテスト書いていけると思う・・・先は長そうだ。
gyazo.com