コアコードのクリーンアップ(6章オブジェクトとデータ構造)


データ構造vsオブジェクト



例Verhicle


データ構造

// 자료 구조
public interface Vehicle {
	double getFuelTankCapacityInGallons();  // 연료탱크 용량(갤런 단위)
	double getGallonsOfGasoline();  // 가솔린 (갤런 단위)
}

public class Car implements Vehicle {
	double fuelTankCapacitInGallons;
	double gallonsOfGasoline;
	public double getFuelTankCapacityInGallons() {
		return this.fuelTankCapacitInGallons;
	}
	public double getFuelTankCapacityInGallons() {
		return this.gallonsOfGasoline;
	}
}
これは、ビジネスロジックが純粋なデータを格納していない資料構造の役割です.

オブジェクト

// 객체
public interface Vehicle {
	double getPercentFuelRemain();
}

public class Car implements Vehicle {
	private double fuelTankCapacitInGallons;
	private double gallonsOfGasoline;

	public Car(double fuelTankCapacitInGallons, double gallonsOfGasoline) {
		if (fuelTankCapcitInGallons <= 0) {
			throw new IllegalArgumentException("fuelTankCapacitInGallons must be greater than zero");
		}
		this.fuelTankCapacitInGallons = fuelTankCapacitInGallons;
		this.gallonsOfGasoline = gallonsOfGasoline;
	}

	public double getPercentFuelRemain() {
		return this.gallonsOfGasoline / this.fuelTankCapacitInGallons * 100;
	}
}
上記のコードは、作成者にビジネスロジックに関連するコードを含み、フィールドをプライベート化して資料を非表示にします.

サンプルシェイプ


データ構造

public class Square {
	public Point topLeft;
	public double side;
}

public class Rectangle {
	public Point topLeft;
	public double height;
	public double width;
}

public class Circle {
	public Point center;
	public double radius;
}

public class Geometry {
	public final double PI = 3.141592653589793;

	public double area(Object shape) throws NoSuchShapeException {
		if (shape instanceof Square) {
			Shape s = (Square)shape;
			return s.side * s.side;
		} else if (shape instanceof Rectangle) {
			Rectangle r = (Rectangle)shape;
			return r.height * r.width;
		} else if(shape instanceof Circle) {
			Circle c = (Circle)shape;
			return PI * c.radius * c.radius;
		}
		throw new NoSuchShapeException();
	}
}
Triangleという新しいクラス(データ構造)が追加されたら?if-else文はarea()関数で変更する必要があります.
  • プログラムコードでは、新しい資料構造を追加することは難しい.
  • 関数を変更する必要があります.
  • オブジェクト

    public class Square implements Shape {
    	private Point topLeft;
    	private double side;
    
    	public double area() {
    		return side * side;
    	}
    }
    
    public class Rectangle implements Shape {
    	private Point topLeft;
    	private double height;
    	private double width;
    
    	public double area() {
    		return height * width;
    	}
    }
    
    public class Circle implements Shape {
    	private Point center;
    	private double radius;
    	public final double PI = 3.141592653589793;
    
    	public double area() {
    		return PI * radius * radius;
    	}
    }
    Triangleというレベルを追加したら?
    Triangleクラスを作成したら、Shapeインタフェースを継承し、Area()関数を上書きします.
    すなわち,オブジェクト向けのコードは新しいクラスを容易に追加できる.ただし、関数を追加する必要があります.

    状況に合った選択をすればいい。


    データ構造

  • 資料構造を用いたプログラムコードは,基本資料構造を変更することなく新しい関数を容易に追加できる.
  • プログラムコードでは、新しい資料構造を追加することは難しい.そのためには、すべての関数を変更する必要があります.
  • オブジェクト

  • オブジェクト向けコードは、既存の関数を変更せずに新しいクラスを追加しやすい.
  • オブジェクトコード向けに新しい関数を追加するのは難しい.これを行うには、すべてのクラスを変更する必要があります.
  • オブジェクト-メジャー・ルール


    クラスCのメソッドfは、以下のオブジェクトのメソッドのみを呼び出すことができます。

  • 類C
  • 自己作成オブジェクト
  • 自己買収の対象
  • Cインスタンス変数に格納オブジェクト
  • A a = new A();
    B b = a.getB()            (O)
    C c = a.getB().getC();    (X)

    公制の法則に反する場合

    // 객체 - 기차 충돌. 디미터의 법칙 위배
    final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
    
    // 자료구조 - OK
    final String outputDir = ctxt.options.scratchDir.absolutePath;
    
    // 객체에 대한 해결책이 아니다. getter를 통했을 뿐, 값을 가져오는 것은 자료구조이다.
    ctxt.getAbsolutePathOfScratchDirectoryOption();
    ctxt.getScratchDirectoryOption().getAbsolutePath();
    
    // 왜 절대 경로를 가져올까.. 근본 원인을 생각해보자!
    // 객체는 자료를 숨기고 자료를 다루는 함수만 공개한다.
    BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);

    DTO


    異なるレイヤ間でデータを交換するために使用


  • 論理はありません.フィールドのみです.

  • 通常クラス名はDto(またはDTO)で終わる.

  • getter/setterもあります.
    - Beans

  • Java bean:データを表すJavaオブジェクト

  • メンバー変数はprivateプロパティです.

  • getterとsetterがあります.
  • public class AddressDto {
    	private String street;
    	private String zip;
    }
    
    public AddressDto(String street, String zip) {
    	this.street = street;
    	this.zip = zip;
    }
    
    public String getStreet() {
    	return street;
    }
    
    public String setStreet(String street) {
    	this.street = street;
    }
    
    public String getZip() {
    	return zip;
    }
    
    public String setZip(String zip) {
    	this.zip = zip;
    }

    Active Record


    データベース行をオブジェクトにマッピングするモード

  • ビジネスロジックメソッドをオブジェクトとして追加することはできません.
  • のビジネスロジックを含む非表示の内部資料のオブジェクトは、個別に生成されます.
  • でも...オブジェクトが多くなると複雑になり、最も近い場所に論理があることが望ましいため、現在の方法はエンティティに簡単な方法を追加することです.
  • public class Employee enxtends ActiveRecord {
    	private String name;
    	private String address;
    	...
    }
    
    // ----
    
    Employee bob = Employee.findByName("Bob Martin");
    
    bob.setName("Robert C. Martin");
    bob.save();

    リファレンス


    これらの情報はゼロベースラインクリーニングコード毎月1冊を聞いた後に整理された.