store-boundsに機能追加した

まえがき

少し前にInkdropのウィンドウサイズと位置を復元するプラグインを作成しました。

ダウンロードが少しされているみたいで嬉しいですね。役に立っていれば幸いです。

今回はちょっとした機能追加をしてみました。

追加した機能

デュアルディスプレイで作業されていて、外付けモニターをメインディスプレイとして設定されている方もいるかと思います。

上記の方向けになってしまいますが、外付けモニターが接続された時にファイルを読み込んでウィンドウサイズと位置を復元出来るようにしました。

些細ではありますが、よく使うモニター側に復元されるといいなと思って追加しました。Stayなどのアプリケーションを利用されている方には不要な機能かもしれません。

今後追加したい機能

  • ダイアログからの数値入力
    • 好みのサイズ・位置に保存し易そう
    • 接続中のモニター境界も設定できると尚良し
  • 今回追加した処理のON・OFF
    • Stayと併用していると動作が不安定になってしまうため

機能もそうだけど、こういったスクリプトのよい書き方が全くわかってないので探っていきたいところ。

ボトムアップDDDを写経した

まえがき

最近、通勤の合間などにIDDDを読んで少しづつDDDの知識を取り入れている。ユビキタス言語や境界づけられたコンテキストなど、概念的な定義が個人的に難しくて読むのに時間がかかっている。

そんな時にnrsさんのブログに書かれている、ボトムアップドメイン駆動設計を読んだ。理解や実践が難しい箇所はひとまず置いておき、理解しやすく実践しやすい箇所からまず始めてみようという内容だ。

自分にぴったりだと思って一通り読んだ後、せっかくなのでPHPとLaravelを使って写経をしてみた。一通り書いてみた感想としては、OOPの特性を活かしたコーディングとはこういうことなんだなとふんわり掴め、責務の所在が明確だとファイルが増えても処理が追いやすいなと感じた。

参考にした記事で扱われているものは次の5つ。どれも技術的な要素なので、普段コードを書いている人にとっては読みやすいと思う。

また、サンプルとは異なる実装をしている箇所を以下で補足する。

変更点

コンストラク

PHP__construct()を複数定義出来ない。そのため、今回はデフォルト引数を用いて実装を行った。

<?php

namespace BottomUpDDD\Domain\Users;

use InvalidArgumentException;
use BottomUpDDD\Common\Util;
use BottomUpDDD\Domain\EquatableInterface;

final class User implements EquatableInterface
{
    /** @var UserId */
    private $id;

    /** @var UserName */
    private $userName;

    /** @var FullName */
    private $fullName;

    public function __construct(
        UserName $userName,
        FullName $fullName,
        UserId $id = null
    ) {
        $this->id = $id === null ? new UserId(Util::guid()) : $id;
        $this->userName = $userName;
        $this->fullName = $fullName;
    }

    // 以下の処理は省略...
}

ただ、必ず引数で受け取る$userName$fullNameを引数としたコンストラクタを定義し、2つの引数に合わせて$idと3つを引数に受け取る静的メソッドを定義すれば擬似的に複数定義することも出来なくはない。

<?php

namespace BottomUpDDD\Domain\Users;

use InvalidArgumentException;
use BottomUpDDD\Common\Util;
use BottomUpDDD\Domain\EquatableInterface;

final class User implements EquatableInterface
{
    /** @var UserId */
    private $id;

    /** @var UserName */
    private $userName;

    /** @var FullName */
    private $fullName;

    public function __construct(
        UserName $userName,
        FullName $fullName
    ) {
        $this->id = new UserId(Util::guid());
        $this->userName = $userName;
        $this->fullName = $fullName;
    }

    public static function ctorWithId(
        UserName $userName,
        FullName $fullName,
        UserId $id
    ) {
        $instance = new self($userName, $fullName);
        $instance->id = $id;
        return $instance;
    }

    // 以下の処理は省略...
}

個人的な意見としては、生成処理のパターンが増えたり、複雑であればファクトリを用意すれば良いのかなという解釈でいる。

等価性の判定

C#JavaのObjectクラスにはequals()というメソッドが定義されており、等価の判別が出来る。これもPHPには用意されていないので、汎用的なメソッドとして用意し、インターフェースを実装した。

<?php

namespace BottomUpDDD\Common;

final class Util
{
    /**
     * @param object $objA
     * @param object $objB
     * @return boolean
     */
    public static function classEquals($objA, $objB): bool
    {
        return get_class($objA) === get_class($objB);
    }

    // 以下の処理は省略...
}
<?php

namespace BottomUpDDD\Domain;

interface EquatableInterface
{
    public function equals(EquatableInterface $obj): bool;
}

リポジトリ

Laravelを使用したということもあり、学習も兼ねてEloquentを用いて実装した。

<?php

namespace BottomUpDDD\ProductionInfrastructure;

use BottomUpDDD\Domain\Users\UserRepositoryInterface;
use BottomUpDDD\Domain\Users\User;
use BottomUpDDD\Domain\Users\UserId;
use BottomUpDDD\Domain\Users\UserName;
use BottomUpDDD\ProductionInfrastructure\Eloquents\UserEloquent;
use BottomUpDDD\Domain\Users\FullName;

final class UserRepository implements UserRepositoryInterface
{
    /** @var UserEloquent */
    private $userEloquent;

    public function __construct(UserEloquent $userEloquent)
    {
        $this->userEloquent = $userEloquent;
    }

    /**
     * @param UserId $userId
     * @return User|null
     */
    public function findByUserId(UserId $userId)
    {
        $target = $this->userEloquent->find($userId->value());

        if ($target === null) {
            return null;
        }

        return new User(
            new UserName($target->user_name),
            new FullName($target->first_name, $target->family_name),
            $userId
        );
    }

    /**
     * @param UserName $userName
     * @return User|null
     */
    public function findByUserName(UserName $userName)
    {
        $target = $this->userEloquent->findByUserName($userName);

        return $target;
    }

    /**
     * @return User[]
     */
    public function findAll()
    {
        $users = $this->userEloquent
            ->all()
            ->map(function (UserEloquent $userEloquent) {
                return new User(
                    new UserName($userEloquent->user_name),
                    new FullName(
                        $userEloquent->first_name,
                        $userEloquent->family_name
                    ),
                    new UserId($userEloquent->id)
                );
            })->toArray();

        return $users;
    }

    public function save(User $user)
    {
        $this->userEloquent->persist($user);
    }

    public function delete(UserId $userId)
    {
        $this->userEloquent->deleteByUserId($userId);
    }
}

あとがき

正直なところ、読む前はリポジトリやエンティティを用いてDDDしてみたというような記事をみかけては、「それって〇〇アーキテクチャを適用しただけでは…?」と思っていた。でも実際書いてみると、かなり歯ごたえがあって実装したときの達成感のようなものがあったので、書きたくなるのもわかるなぁという感じ。こうして自分も記事にしているので。

ただ参考記事や書籍にも書かれているように、ユビキタス言語や境界づけられたコンテキストなどの概念がより重要であり、技術的な側面だけを注力するといわゆる軽量DDDに陥ってしまうので、戦略的設計に関わる知識もインプットしていく。

軽量DDDとは、DDDの戦術的パターンの一部だけをつまみ食いする方法で、ユビキタス言語を見つけ出して育てていこうなどという気は毛頭ない。おまけにこの手法は、境界づけられたコンテキストやコンテキストマッピングも使わずに済ませることが多い。技術面だけに注目して、技術的な問題だけを解決したがっているのだ。この方法にはメリットもあるが、戦略的モデリングを併用したときのような大きな見返りは得られない。(実践ドメイン駆動設計より)

最後に写経したものはGitHubに置いているので、参考になれば。

nrsさんのサンプルコードはこちら。(C#)

参考URL

Inkdropのウィンドウサイズを保存するようにした

普段PCでメモなどを書く時にInkdropを使っている。有料ではあるが、プラグインやテーマの追加が出来たり、データの同期が出来たり等、便利な点が多い。

しかしながら、アプリケーションの起動時にウィンドウサイズが保持されておらず、起動するたびにサイズを変更している。フォーラムにも書き込まれているがまだ未対応の模様。プラグインの追加は簡単に出来るため、electronの知識は無いが調べながら作ってみた。

安直にsetBounds()を呼び出しても、起動時のサイズに戻ってしまうので、center()とmovedイベントを組み合わせて無理やりサイズを変更させている。リサイズ時がかなり不格好なので製作者さんに対応してもらった方が良いのかもしれない。

参考

2018-07-28

せっかくなので、その日(でない場合もある)に閲覧したリンクの内容をまとめておく。

アジャイル開発でのソフトウェア設計 - Speaker Deck

  • 継続的に設計を行い、その時に適した設計をソフトウェアに適用していく
    • レビューや経験、試行錯誤を通して成熟させていく
  • 必要な設計だけを行う
  • アジリティを重視した継続的な設計

ソフトウェアは大抵、改善が繰り返されるので、ゴールと呼べる状態がないというのはその通りだなと思う。ただ、理想の設計はあって、それに近づける為にその時々で妥当な設計を適用しているのではないかと個人的には思った。

DDD vs Clean architecture: hosting the business logic – Object Code 101

  • DDDではコンテキストごとにドメインオブジェクトを作成する
  • Clean Architectureでは基本的な振る舞いを持つドメインオブジェクトを作成し、ユースケースオブジェクトで利用する
  • DDD、Clean Architectureはドメインオブジェクトを重視しているという点で非常に似ている
  • どちらも外部の依存関係(ライブラリやフレームワーク等)からドメインを分離している

久しぶりにソシャゲをがっつりした一日だった…。

AngularのプロジェクトをGithub Pagesに公開する

AngularのプロジェクトをGithub Pagesにデプロイしたときのメモ。

angular-cli-ghpagesというパッケージを使用することで簡単にデプロイ出来た。

インストール

コマンドの利用するには、次の条件を満たしておく必要がある。

  • Node.js: 4.x以上
  • Git: 1.7.6以上
  • (オプション)angular-cliで作成されたプロジェクト

今回実行した環境は次の通り。

使い方

angular-cli v6だと、ビルド時のディレクトリ構成がdist/REPOSITORY_NAMEとなっている。

そのため、ng buildもしくはangular-cli-ghpagesディレクトリを指定する必要がある。

今回はangular-cli-ghpagesのオプションを使用した。

ng build --prod --base-href "https://USERNAME.github.io/REPOSITORY_NAME/"
angular-cli-ghpages --dir="dist/REPOSITORY_NAME"

もしくは、

ng build --prod --base-href "/REPOSITORY_NAME/"
angular-cli-ghpages --dir="dist/REPOSITORY_NAME"

もしくは、

# <base href="">となる
ng build --prod
angular-cli-ghpages --dir="dist/REPOSITORY_NAME"

を実行すると、リモートへプッシュが行われる。

angular-cli-ghpagesの代わりにnghでも実行可能。

オプション

コマンド実行時にいくつかオプション設定が可能。

使用したものだけ記載しているので、他のオプションについてはREADME.mdを参照してください。

--message

  • コミット時のメッセージを設定。デフォルトはAuto-generated commit
  • e.g. ngh --message="New message"

--dir

  • デプロイするディレクトリを設定。デフォルトはdist
  • angular-cli v6だと"outputPath": "dist/REPOSITORY_NAME"となっているので注意
  • e.g. ngh --dir="dist/REPOSITORY_NAME"

--branch

  • プッシュ先のブランチ名を設定。デフォルトはgh-pages
  • e.g. ngh --branch=other-branch

Gihub Pagesの設定

nghでプッシュされたのが確認出来れば以下を設定する。

  1. GithubリポジトリSettingsタブをクリック。
  2. Github Pagesの項目までスクロールし、Sourceからプッシュしたブランチを設定。
  3. URLにアクセスしてページが表示されれば完了!

参考URL

Visual Studio Codeの設定 - キーマップ

VS Codeのキーマップ編。拡張機能についてはこちら

HHKBもしくは類似したキーマップの設定をしていると使いやすい設定だと思います。

エディタ関連

タブの移動

デフォルトの設定だとshift+cmd+[shift+cmd+]に割り当てられているが、[]は右手のホームポジションであるjkに、shiftctrlに変えて割り当て直した。

サジェスト表示

macだとデフォルトでctrl+spaceに割り当てられているが、英字キーボードの場合はIMEの変換に使用されているため、配置が近いshift+spaceに割り当て。

サイドバー関連

ツリー表示を全て閉じる

node_modules、vendorのファイルに定義ジャンプをするとサイドバーがかなりごちゃごちゃするので設定した。サイドバーへのフォーカスがcmd+0だったので、shift+cmd+0と似たキーマップにした。

サイドバーからファイルを開けるようにする

いつのバージョンからかこの設定がなくなってしまっていた。cmd+pでもファイルを開けるが、場合によってはこの操作の方が楽だったりする時も個人的にはある。設定は以前と同じくcmd+enterに割り当て。参考にしたリンクはこちら

2018-06-29 追記

renamefilelist.selectがどちらもenterでデフォルト設定されていて、前者が優先されて動作しなくなっていた。cmd+enterで設定していたが、Lにもlist.selectが設定されていたのでこっちを使ったほうが早い。

新規フォルダ作成

使用頻度は低いが、ボタンクリックする手間が省けるのでctrl+shift+nに設定している。新規ファイル作成に関しては、拡張機能advanced-new-fileを使用している。

gistに公開したので、参考程度にどうぞ。

Visual Studio Codeの設定 - 拡張機能

よく見かけるインストールしている拡張機能を書いた記事。

共通

Settings Sync

gistにインストールしている拡張機能スニペット、キーマップなどを保存してくれるので便利。

Vim

vimキーバインド。慣れないうちは辛いけど、少しでも操作を覚えると快適。

Bookmarks

任意の行をブックマークできる。ファイルの行き来が増えてくると特定行にすぐジャンプ出来るので便利。

Path Intellisense

ファイルパスを補完表示してくれる。jsやAngularのimport書くのが捗る。

Nord

カラーテーマ。Slime ThemeOne Dark Proを試した結果、上記のテーマに落ち着いた。寒色が好きなので良い感じ。

Bracket Pair Colorizer

ブラケットに色をつけてくれる。インストールしてからブロックがとてもわかりやすい。

PHP

php cs fixer

コード整形。

PHP Intelephense

コード補完。@returnに型をつけておけば、返り値側でメソッドなども補完してくれるので、次のPHP DocBlockerと組み合わせて使っている。

PHP DocBlocker

PHP Doc形式のコメント補完。@paramや@returnを自動挿入してくれるので楽。

PHP Debug

デバッガ。変数、ウォッチ式、呼出履歴が確認出来る。ステップ実行も出来て便利。

Angular

Angular 7 Snippets - TypeScript, Html, Angular Material, ngRx, RxJS & Flex Layout

コード補完。

Angular Language Service

テンプレートでのプロパティ補完。

Prettier - Code formatter

js, ts, cssのコード整形。

Change Log

2018-10-28

  • Angular 6 SnippetsからAngular 7 Snippetsに名称変更
  • カラーテーマをOne Dark ProからNordに変更
  • 共通にBracket Pair Colorizerを追加