응집도
: 한 프로그램의 요소가 얼마나 뭉쳐있는지결합도
: 프로그램 구성 요소 사이가 얼마나 의존적인지Before
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
/**
getter, setter
**/
}
class UserSetting {
private User user;
public UserSetting(User user) {
this.user = user;
}
public void changeUserName(String name){
if (upToNineteen()) {
user.setName(name);
}
}
public boolean upToNineteen() {
return this.user.getAge() >= 19;
}
}
UserSetting
은 사용자의 이름을 변경하는 changeUserName()
과 사용자의 나이가 19세 이상인지 확인하는 upToNineteen()
의 역할(책임)이 두 가지가 있다. 한 클래스가 두 개의 책임을 갖는 것은 SRP
에 위배되는 행위다.After
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
/**
getter, setter
**/
}
class UserSetting {
private final User user;
public UserSetting(User user) {
this.user = user;
}
public void changeUserName(String name) {
if (UserVerify.upToNineteen(user)) {
this.user.setName(name);
}
}
}
class UserVerify {
public static boolean upToNineteen(User user) {
return user.getAge() >= 19;
}
}
UserSetting
은 이름을 변경하는 역할만을 갖고, 새로 만든 클래스 UserVerify
는 나이가 19세 이상인지 판별하는 역할만을 가진다. 두 가지로 나누었기 때문에 SRP
를 위반하지 않는다.closed
) 기능을 추가할 수 있도록 (open
) 설계되어야 한다.interface
or abstract class
로 구현캐스팅
과 if-else
구문에서 위배되는 경우 발생Before
class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public getName(){
return this.name;
}
}
class MyAnimal {
List<Animal> animals = new ArrayList<>();
public void animalSound(){
for (Animal animal : animals) {
if (animal.getName().equals("lion")) {
System.out.println("roar");
} else if (animal.getName().equals("mouse")) {
System.out.println("zikzik");
} else if (animal.getName().equals("snake")) {
System.out.println("hiss");
} else {
System.out.println("etc.");
}
}
}
}
"dog"
를 추가하면 MyAnimal.animalSound()
를 아래와 같이 dog
에 대한 내용을 추가해야 한다.public void animalSound(){
for (Animal animal : animals) {
if (animal.name.equals("lion")) {
System.out.println("roar");
} else if (animal.name.equals("mouse")) {
System.out.println("zikzik");
} else if (animal.name.equals("snake")) {
System.out.println("hiss");
} else if (animal.name.equals("dog")) {
System.out.println("bowwow");
} else {
System.out.println("etc.");
}
}
}
After
import java.util.ArrayList;
import java.util.List;
interface Animal {
void makeSound();
}
class Lion implements Animal {
@Override
void makeSound() {
System.out.println("roar");
}
}
class Mouse implements Animal {
@Override
void makeSound() {
System.out.println("zikzik");
}
}
class Snake implements Animal {
@Override
void makeSound() {
System.out.println("hiss");
}
}
class Dog implements Animal {
@Override
void makeSound() {
System.out.println("bowwow");
}
}
class MyAnimal {
List<Animal> animals = new ArrayList<>();
public void animalSound(){
for (Animal animal : animals) {
animal.makeSound();
}
}
}
MyAnimal
에 대한 코드 수정은 없고 (closed
), 새로운 동물 추가에 대한 확장은 열려있다. (open
)OCP
를 위반하지 않도록 하는 원칙Before
class Bag {
private int price;
public void setPrice(int price){
this.price = price;
}
}
class DiscountedBag extends Bag {
private double discountedRate = 0.0;
public void setDiscounted(double dc) {
discountedRate = dc;
}
@Override
public void setPrice(int price) {
super.setPrice(price - (int) (discountedRate * price));
}
}
Bag
에 존재하는 메서드 setPrice()
를 자식 클래스인 DiscountedBag
에서도오버라이딩
해서 사용했다. 이는 LSP
를 위반하는 행위다.After
class DiscountedBag extends Bag {
private double discountedRate = 0.0;
public void setDiscounted(double dc) {
discountedRate = dc;
}
public void setDiscountedPrice(int price) {
super.setPrice(price - (int) (discountedRate * price));
}
}
setDiscountedPrice(...)
과 같이 새로운 메서드를 형성하여 오버라이딩
을 피했기 때문에 LSP
를 지킬 수 있었다.Before
interface Calculator {
void add();
void subtract();
void multiply();
}
class CalculatorRecentVer implements Calculator {
@Override
public void add() {
System.out.println("+");
}
@Override
public void subtract() {
System.out.println("-");
}
@Override
public void multiply() {
System.out.println("*");
}
}
class CalculatorPastVer implements Calculator {
@Override
public void add() {
System.out.println("+");
}
@Override
public void subtract() {
System.out.println("-");
}
//필요없는 메서드
@Override
public void multiply() {
System.out.println(".........");
}
}
Calculator
에 현재 3개의 연산이 담겨있지만 과거 계산기에는 multiply()
가 없었다. 필요치 않은 기능을 구현한 Interface
이기 때문에 ISP
를 위반했다.After
interface Calculator {
void add();
void subtract();
}
interface CalculatorRecent {
void multiply();
}
class CalculatorPastVer implements Calculator {
@Override
public void add() {
System.out.println("+");
}
@Override
public void subtract() {
System.out.println("-");
}
}
class CalculatorRecentVer implements Calculator, CalculatorRecent {
@Override
public void add() {
System.out.println("+");
}
@Override
public void subtract() {
System.out.println("-");
}
@Override
public void multiply() {
System.out.println("*");
}
}
CalculatorResent
인터페이스를 추가하고 최신 계산기에만 해당 인터페이스를 상속받아 *
에 대한 연산을 가능하도록 구현했다. 이는, ISP
를 지키는 행위다.Before
class Americano {
void drink() {
System.out.println("americano");
}
}
class Latte {
void drink() {
System.out.println("latte");
}
}
class Water {
void drink() {
System.out.println("water");
}
}
class Person extends Americano {
//
}
public class DrinkSome {
public static void main(String[] args) {
Person p = new Person();
p.drink(); //아메리카노만 마실 수 있는 사람...
}
}
Person
이 구체적인 Americano
만 의존한다. 이는, DIP
에 위배되는 행위다.After
interface SomeDrink {
void drink();
}
class Americano implements SomeDrink {
@Override
public void drink() {
System.out.println("americano");
}
}
class Latte implements SomeDrink {
@Override
public void drink() {
System.out.println("latte");
}
}
class Water implements SomeDrink {
@Override
public void drink() {
System.out.println("water");
}
}
class Person {
SomeDrink ame = new Americano();
SomeDrink latte = new Latte();
SomeDrink water = new Water();
public void drinkAmericano(){
ame.drink();
}
public void drinkLatte(){
latte.drink();
}
}
public class DrinkSome {
public static void main(String[] args) {
Person p = new Person();
p.drinkAmericano();
}
}
Person
은 SomeDrink
의 인스턴스화를 통해 여러 객체를 생성 및 초기화할 수 있고 main
에서는 필요한 메서드만을 사용할 수 있도록 했다.