ukiyuの思考研究所。

人生道半ばでの知識の棚卸し、次のお仕事決まるまで。オブジェクト指向モデリングとかに関してつらつらと。ご意見、ご感想等は気楽にどうぞ。。。

四角を動かすだけ(4)。

mdl.hatenablog.com

前回↑の続き。

ひとまず、四角を動かすことは出来ました。
次に何か適当な機能を追加してみようと思います。

何でもいいんですが、PositionとSizeを定義しておいてSizeは何もしていないので、取りあえず大きさを変えるような機能を追加します。

さっくり書くとやりたいのは、

// class Service
		Rectangle resizedRactangle = rectangle.resize(1.5, 1.5);

こういうことですかね。
その為にRectangleにresizeメソッドを追加します。

ここでもキッチリget()せずに求めるな、命じよを守って

// class Rectangle
	public Rectangle resize(double rateX, double rateY) {
		return of(position, size.multiply(rateX, rateY));
	}

Sizeに掛け算するように命じます。
なので、Sizeにも掛け算のメソッドを追加することになります。

// class Size
	public Size multiply(double x, double y) {
		return of(height * x, width * y);
	}

と、ただそれだけなのですが、またしても x,y が常に同時に扱われているし、doubleだし、サイズを変更する責務を切り出して新しいクラスを導入してみます。適切にクラス化することで演算の誤りを防ぐことが出来ます。既に位置や大きさはクラス化されていますが、倍率と共にdoubleのままだとすると、足し算や引き算など望ましくない演算が可能となってしまいます。クラス化することで意図された演算のみをメソッドとして実装することで、予め不適切な演算を防ぎます。

今回はこの掛け算はSizeにのみ適用される演算であるとして、Sizeの内部クラスにします。
もちろん、Positionにも適用される共通な演算とみなす場合は内部クラスとするのは不適切です。

// class Service
		Size.Rate rate = Size.Rate.of(1.5);
		Rectangle resizedRactangle = rectangle.resize(rate);
// class Rectangle
	public Rectangle resize(Size.Rate rate) {
		return of(position, size.multiply(rate));
	}
// class Size
	static public class Rate {
		private final double x;
		private final double y;

		private Rate(double x, double y) {
			this.x = x;
			this.y = y;
		}

		static public Rate of(double x, double y) {
			return new Rate(x, y);
		}

		static public Rate of(double x) {
			return new Rate(x, x);
		}

		private Size apply(double height, double width) {
			return Size.of(height * x, width * y);
		}
	}

	public Size multiply(Rate rate) {
		return rate.apply(height, width);
	}

新たなクラスRateを導入することで、掛け算の倍率を管理することはRateの責務となります。
それをget()するようなことはせずに、倍率を適用するように命じることがポイントです。

機能追加後。

package com.ukiyu.blog008;

public class Service {

	public static void main(String[] args) {
		System.out.println("Ver.008 Start");

		Position position = Position.of(100, 100);
		Size size = Size.of(20, 30);
		Rectangle rectangle = Rectangle.of(position, size);
		System.out.println(rectangle);

		Position distance = Position.of(10, 20);
		Rectangle movedRactangle = rectangle.plus(distance);

		System.out.println(movedRactangle);

		Size.Rate rate = Size.Rate.of(1.5);
		Rectangle resizedRactangle = rectangle.resize(rate);

		System.out.println(resizedRactangle);
	}
}
package com.ukiyu.blog008;

public class Rectangle {
	private final Position position;
	private final Size size;

	private Rectangle(Position position, Size size) {
		this.position = position;
		this.size = size;
	}

	static public Rectangle of(Position position, Size size) {
		return new Rectangle(position, size);
	}

	public Rectangle plus(Position other) {
		return of(position.plus(other), size);
	}

	public Rectangle plus(Size other) {
		return of(position, size.plus(other));
	}

	public Rectangle resize(Size.Rate rate) {
		return of(position, size.multiply(rate));
	}

	@Override
	public String toString() {
		return "Rectangle " + position + ", " + size;
	}
}
package com.ukiyu.blog008;

public class Position {
	private final double x;
	private final double y;

	private Position(double x, double y) {
		this.x = x;
		this.y = y;
	}

	static public Position of(double x, double y) {
		return new Position(x, y);
	}

	public Position plus(Position other) {
		return of(x + other.x, y + other.y);
	}

	@Override
	public String toString() {
		return "(x, y) = (" + x + ", " + y + ")";
	}
}
package com.ukiyu.blog008;

public class Size {
	private final double height;
	private final double width;

	private Size(double height, double width) {
		this.height = height;
		this.width = width;
	}

	static public Size of(double height, double width) {
		return new Size(height, width);
	}

	public Size plus(Size other) {
		return of(height + other.height, width + other.width);
	}

	static public class Rate {
		private final double x;
		private final double y;

		private Rate(double x, double y) {
			this.x = x;
			this.y = y;
		}

		static public Rate of(double x, double y) {
			return new Rate(x, y);
		}

		static public Rate of(double x) {
			return new Rate(x, x);
		}

		private Size apply(double height, double width) {
			return Size.of(height * x, width * y);
		}
	}

	public Size multiply(Rate rate) {
		return rate.apply(height, width);
	}

	@Override
	public String toString() {
		return "(height, width) = (" + height + ", " + width + ")";
	}
}

コンソール出力。

Ver.008 Start
Rectangle (x, y) = (100.0, 100.0), (height, width) = (20.0, 30.0)
Rectangle (x, y) = (110.0, 120.0), (height, width) = (20.0, 30.0)
Rectangle (x, y) = (100.0, 100.0), (height, width) = (30.0, 45.0)

コードはこちら↓。
https://github.com/ukiyu/blog/tree/master/ForBlog/src/com/ukiyu/blog008

(100, 100)の位置にあった(20, 30)の大きさの四角形が1.5倍になりました。ちゃんとモデリングすることでソースコードもそれをそのまま表現したものになっているかと思います。getter/setter を多用するコードにはそのような表現力はありません。

次回もまた適当な機能追加を行いたいと思います。

続く。