TypeScriptでオブジェクト指向実践 その①


オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方
を読んでいるのでメモがてらにまとめてみる。

RubyではなくTypeScriptで実装してみた。

モデル

1. 変数を隠蔽する

  変数をprivateにし、外部からの取得はgetterにする。これにより直接変数がいじられることを防げるのと、プライベート変数に追加で1.2倍係数を掛けたいなどの要望があった時はgetter内をいじるだけでよい。

Gear1.ts

export class Gear {

    private PI = 3.14;
    constructor(private _chainring: number, private _cog: number,private _rim: number, private _tire: number) {
    }

    get rim() {
        return this._rim;
    }

    get tire() {
        return this._tire;
    }

    get cog(): number {
        return this._cog;
    }

    get chainring() {
        return this._chainring;

    }

    diameter() {
        return this.rim + this.tire * 2;
    }

    public ratio(): number {
        return this.chainring / this.cog
    }

    public gearInches() {
            return this.ratio() * this.diameter();
    }
}

2.依存関係をクラスに分ける

  1のクラスではギア(Gear)に関するメソッドに車輪(Wheel)についての情報(rim,tire)までも含まれていた。これは単一責任の原則(SRP)の反するし、拡張性のために分離シておいたほうが良い。

Gear2.ts
import { Wheel } from "./Wheel";


class Gear {
    constructor(private _chainring: number, private _cog: number, private wheel?: Wheel) {
    }

    get cog(): number {
        return this._cog;
    }

    get chainring() {
        return this._chainring;

    }

    public ratio(): number {
        return this.chainring / this.cog
    }

    public gearInches() {
        if (this.wheel) {
            return this.ratio() * this.wheel.diameter();
        }
        return null;
    }
}



Wheel.ts
export class Wheel {
    private PI = 3.14;

    constructor(private _rim: number, private _tire: number) {
    }

    get rim() {
        return this._rim;
    }

    get tire() {
        return this._tire;
    }

    diameter() {
        return this.rim + this.tire * 2;
    }

    cicle() {
        return this.diameter() * this.PI;
    }
}

3.引数をハッシュにする

 上記までは引数の順番を知らなくては正しくクラスを使えなかった。そのため、引数としてわかりやすくパラメータを与えることにする。

Gear3.ts
import { Wheel } from "./Wheel";

export class Gear {
    private readonly _chainring:number;
    private readonly _cog: number;
    private readonly wheel?: Wheel;

    constructor(args: any) {
        this._chainring = args['chainring']
        this._cog = args['cog']
        this.wheel = args['wheel']
    }

    get cog(): number {
        return this._cog;
    }

    get chainring() {
        return this._chainring;

    }

    public ratio(): number {
        return this.chainring / this.cog
    }

    public gearInches() {
        if (this.wheel) {
            return this.ratio() * this.wheel.diameter();
        }
        return null;
    }
}

このクラスの使い方はこう

const args = {
    chainring: 10,
    cog:10,
    wheel: new Wheel(10,10)
}
const gear = new Gear(args);

console.log(gear.gearInches()) // => 30

3.Factoryクラス

外部のクラスなど自分でいじれない場合、引数の順番を変えたりハッシュ(JSON形式)にできないので、Factoryクラスでラップすることで解決する。

GearFactory.ts
import { Wheel } from "./Wheel";

class GearFactory {

    constructor(private args:any) {
    }
    gear(): Gear{
        return new Gear(this.args['chainring'],this.args['cog'],this.args['wheel'])
    }
}

export class Gear {
    constructor(private _chainring: number, private _cog: number, private wheel?: Wheel) {
    }

    get cog(): number {
        return this._cog;
    }

    get chainring() {
        return this._chainring;

    }

    public ratio(): number {
        return this.chainring / this.cog
    }

    public gearInches() {
        if (this.wheel) {
            return this.ratio() * this.wheel.diameter();
        }
        return null;
    }
}

const args = {
    chainring: 10,
    cog:10,
    wheel: new Wheel(10,10)
}
const gear = new GearFactory(args).gear();

console.log(gear.gearInches()); //=> 30

いまのところ以上!!

 まだ導入なのでオブジェクト指向というよりリファクタリングっぽいが、今後オブジェクト指向実践になるのだろうか!!!