객체지향 설계 5대 원칙 SOLID - shinminsoo118/solid_5 GitHub Wiki
1) SRP - 단일책임원칙
- 단일 클래스는 단 한개의 책임을 가져야 한다. (클래스를 변경하는 이유는 단 한개여야 한다.)
before
public class UserSettingService {
public void changeEmail(User user) {
if(checkAccess(user)) {
// Grant option to change
}
}
public boolean checkAccess(User user) {
// Verify if the user is valid.
}
}
after
public class UserSettingService {
public void changeEmail(User user) {
if(SecurityService.checkAccess(user)) {
// Grant option to change
}
}
}
public class SecurityService {
public static boolean checkAccess(User user) {
// Check the access.
}
}
2. OCP - 개방폐쇄원칙
- 기능을 변경하거나 확장할 수 있으면서 그 기능을 사용하는 코드는 수정하지 않는다.
before
public class Rectangle {
public double Width { ... }
public double Height { ... }
}
public class AreaCalculator {
public double Area(object[] shapes) {
double area = 0;
foreach (shape instanceof shapes) {
if (shape instanceof Rectangle) {
Rectangle rectangle = (Rectangle) shape;
area += rectangle.Width * rectangle.Height;
}
else {
Circle circle = (Circle)shape;
area += circle.Radius * circle.Radius * Math.PI;
}
}
return area;
}
}
after
public interface class Shape {
double Area();
}
public class Rectangle implements Shape {
public double Width { ... }
public double Height { ... }
@Override
public double Area() {
return Width*Height;
}
}
public class Circle implements Shape {
public double Radius { ... }
@Override
public double Area() {
return Radius * Radius * Math.PI;
}
}
public double Area(Shape[] shapes) {
double area = 0;
foreach (var shape in shapes) {
area += shape.Area();
}
return area;
}
3. LSP - 리스코브 치환원칙
- 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
before
public class Coupon {
public int calcuateDiscountAmount(Item item) {
if (item instanceof SpecialItem) // LSP 위반
return 0;
return item.getPrice() * discountRate;
}
}
after
public class Item {
public boolean isDiscountAvailable() {
return true;
}
}
public class SpecialItem extends Item {
// 하위 타입에서 알맞게 오버라이딩
@Override
public boolean isDiscountAvailable() {
return false;
}
}
public class Coupon {
public int calcuateDiscountAmount(Item item) {
if (!item.isDiscountAvailable()) // instanceof 연산자 제거
return 0;
return item.getPrice() * discountRate;
}
}
4. ISP - 인터페이스 분리원칙
- 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.
before
// 하나의 인터페이스를 모든 클라이언트가 구현하고 있음
public interface ArticleService {
void list();
void write();
void delete();
}
public class UiList implements ArticleService {
@Override
public void list() {}
@Override
public void write() {}
@Override
public void delete() {}
}
public class UiWist implements ArticleService {
@Override
public void list() {}
@Override
public void write() {}
@Override
public void delete() {}
}
public class UiDist implements ArticleService {
@Override
public void list() {}
@Override
public void write() {}
@Override
public void delete() {}
}
after
// 각각의 클라이언트별로 Interface를 구분
public interface ArticleListService {
void list();
}
public interface ArticleWriteService {
void Write();
}
public interface ArticleDeleteService {
void Delete();
}
public class UiList implements ArticleListService {
@Override
public void list() {}
}
public class UiWist implements ArticleWriteService {
@Override
public void write() {}
}
public class UiDist implements ArticleDeleteService {
@Override
public void delete() {}
}
5. DIP - 의존역전원칙
- 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안된다. 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야 한다.
- 고수준모듈(어떤 의미있는 단일 기능을 제공하는 모듈)
- 바이트 데이터를 읽어와 암호화하고 결과 바이트 데이터를 쓴다.
- 저수준모듈(고수준 모듈의 기능을 구현하기 위해 필요한 개별기능. 좀 더 작은모듈)
- 파일에서 바이트 데이터를 읽어온다
- AES 알고리즘으로 암호화 한다.
- 파일에 바이트 데이터를 쓴다.
- key point 중간에 interface와 같은 추상화를 고수준 모듈과 저수준 모듈이 모두 추상 타입에 의존하게 만든다.
before
// "Low level Module" Mechanism equivilant
public class Logger {
public void logInformation(String logInfo) {
System.out.println(logInfo);
}
}
// "High level module" Policy equivalent.
public class Foo {
// direct dependency of a low level module.
private Logger logger = new Logger();
public void doStuff() {
logger.logInformation("Something important.");
}
}
after
public interface ILogger {
void logInformation(String logInfo);
}
public class Logger implements ILogger {
@Override
public void logInformation(string logInfo) {
System.out.println(logInfo);
}
}
public class Foo {
private ILogger logger;
public void setLoggerImpl(ILogger loggerImpl) {
this.logger = loggerImpl;
}
public void doStuff() {
logger.logInformation("Something important.");
}
}
// Usage
Foo foo = new Foo();
ILogger logger = new Logger();
foo.setLoggerImpl(logger);
foo.doStuff();