캡슐화 하는 이유 : 남들이 변수에 의존하지 않고, 추상화된 메소드만을 통해서 프로그램을 서술해 나가도록 할 수 있음
그래서 캡슐화를 왜 해야하는지 그럼, 무작정 캡슐화를 하면 되는걸까 라고 의문점을 던지는 것으로 시작해서 객체지향적인 풀이법이 마냥 해답은 아니다. 어떤 패러다임이나 방식으로도 유연한 설계를 구현할 수 있다면 최선의 해결책을 선택해라 라는걸 전반적인 주제
//자료 6-1
public class Point{
public double x;
public double y;
}
//자료 6-2
public interface Point {
double getX();
double getY();
void setCartesian(double x, double y);
double getR();
double getTheta();
void setPolar(double r, double theta);
}
6-2 에서는 어떤 좌표계를 사용하는지 알 길이 없다.
그러나 6-2에서는 자료 구조를 명백하게 표현한다.
x
,y
를 변수로 가지는것을 확실히 볼 수 있으니까?근데 적절한 예제라기 보다는 설명을 위한 예제라는 느낌을 받았고.
개인적인 생각으로는 메소드를 위와 같이 정의했다라는 것은 2가지 기능 다 구현을 해야 한다 생각이 들고
UnSupportedOperationException
를 써야 한다면, 인터페이스를 분리 하는게 맞지 않을까 라는 생각
결국 이 로버트 마틴 C 가 말하고자 하는 부분은
정의와 구현을 하나의 클래스로 두면, 개발자 입장에서는 결국 그 기능을 알기 위해서는 코드까지 들어가봐야 하고 그러다보면 생산성이 떨어질 수도 있다라는걸 말하는게 아닌가 생각이 들었고,
이러한 불편함을 해결하기 위해서 추상인터페이스와 그것을 구현한 클래스를 둠으로써 정의부와 구현부를 분리하는 브릿지 패턴을 사용하라 라는 느낌을 받았다.
그렇게 분리를 해두면 추상인터페이스만 보고도 어떻게 동작하고, 어떤 객체를 리턴하는지 확인할 수 있으니까?
//6-3 구체적인 Vehicle 클래스
public interface Vehicle {
double getFuelTankCapacityInGallons();
double getGallonsOfGasoline();
}
//6-4 추상적인 Vehicle 클래스
public interface Vehicle {
double getPercentFuelRemaining();
}
이거는 여기에 대해서 할말은 딱히 없음. 대신 설명을 위한 ㅇㅖ제라는 느낌을 받음 사실 6-3도 별로 이상할 것 없는 느낌..?
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.14;
public double area(Object shape) throws NoSuchShapeException{
if(shape instanceof Square){
Square s = (Suare)shape;
return s.side * s.side;
}else if (shape instanceof Rectangle){
Rectangle r = (Rectangle) shape;
return r.height * r.width;
}else if (shape instanceof Circe){
Circle c = (Circle)shape;
return PI * c.radius * c.radius;
}
throw new NoSuchShapeException();
}
}
}
Geometry.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;
}
}
그리고 이 둘이 근본적으로 책에서 어떤 차이점이 있냐고 말하면
절차 지향 같은 경우는 함수 추가 하고, 지원하지 않는 부분에 대해서 NoSuchShapeException 을 때리면 되니까
클래스 같은 경우는 함수를 추가하는데 있어, 상속받는 클래스들에 대해서 모조리 구현을 추가하면 되니까
물론 상속받는 클래스들에 대해서 모조리 구현을 추가하지 않고 해결하는 방법으로 Visitor 패턴이 있다고함
public class ShapeVisitor {
private constrouctor(){
}
public static double area(Rectangle rectangle){
return rectangle.getHeight() * rectangle.getWidth();
}
public static double area(Square square){
return square.getSide() * square.getSide();
}
//만약 상속받는 클래스가 한개 더 생기면 여기에다 그대로 정의해주고 사용만 하면 되니까~~
public static double area(임의의 클래스 R){
throw new UnSupportedOperationException();
}
}
public Class C{
private int a;
private double b;
//가능.1
public String f(AnotherClass parameter){
double result = parameter.getData() + this.a + this.b;
return Double.toString(result);
}
//불가능
/*
* f가 생성한 객체가 또 다른 객체를 생성하고 있기 때문에 디미터 법칙에 어긋남.
*/
public String f(AnotherClass parameter){
double result = parameter.getData().getNum() + this.a + this.b;
return Double.toString(result);
}
//가능2
public String f(AntotherClass parameter){
double result = parameter.getDataNum() + this.a + this.b;
return Double.toString(result);
}
}
즉, 디미터 법칙을 만족시키기 위해선 AnotherClass에서 생성한 객체를 호출한 객체에서 사용할 수 있도록 클래스를 만들어줘야함
@Getter // 자동으로 getEmail(), getName(), getAddress()를 만들어주는 표현식이라 보면됨
public class User{
private String email;
private String name;
private Address address;
}
public class Address {
private String region;
private String details;
}
public class SendService{
//디미터 법칙 어긋남
public void sendMessageToSeoulRegion(User user){
if("서울".equals(user.getAddress().getRegion())){
sendMessage();
}
}
//디미터 법칙 만족
public void sendMessageToSeoulRegion(User user){
if(user.isSeoulRegion()){
sendMessage();
}
}
}
// User에 method 추가 및 어떤 리전인지 확인하는 책임을 부여해줘서 디미터 법칙 만족
public class User{
//생략
public boolean isSeoulRegion(){
return "서울".equals(this.address().getRegion());
}
}
그리고 A.getB().getC().getD() 과 같이 계속 호출에 호출을 하는 것을 기차 충돌이라고 한다고 하니 기억해두면 될듯.
자료 구조체의 전형적인 형태로 공개 변수만 있고 함수가 없는 클래스.
public class DTO {
public int a;
public double b;
public String c;
}
이책에서 말하는 DTO는 VO, DTO, Entity를 하나로 합친듯한 느낌이 많이 들음.
책에서 말하는 DTO는 다음과 같음
데이터 베이스에 저장된 가공되지 않은 정보를 어플리케이션 코드에서 사용할 객체로 변환하는 일련의 단계에서 가장 처음으로 사용하는 구조체
공개 변수만 존재
그래서 내가 알고있는 VO, DTO, Entity에 대해서 먼저 설명을 해보겠음
종류 | 설명 |
---|---|
VO (Value Object) | 읽기 전용으로 값을 조작할 수 없음, 더이상 객체의 변경이 없는 최종적인 형태 일때 사용 (view로 보내줄때) |
DTO(Data Transfer Object) | 비즈니스 코드간의 데이터를 전달할 때 사용하는 클래스 |
Entity | DB와 직접적인 데이터를 주고 받을 때 사용하는 클래스 |
그림으로 표현하자면 이렇게 되겠네
예제는 서버단에 적합한 구조이지 않을까 생각이 들어서 생략