Written by JS970
on
on
소프트웨어시스템설계 2023-03-15 수업정리
Flow
- Refactoring (2)
Refactoring Example
아래의 UML과 코드들은 리펙토링을 하기 이전의 원본 상태이다.
Original UML
Original Code - Run
Customer customer1 = new Customer("David");
Movie movie1 = new Movie("Madagascar", Movie.CHILDRENS);
Rental rental1 = new Rental(movie1, 6);
Movie movie2 = new Movie("Star Wars", Movie.NEW_RELEASE);
Rental rental2 = new Rental(movie2, 2);
Movie movie3 = new Movie("Gone with the Wind", Movie.REGULAR);
Rental rental3 = new Rental(movie3, 8);
customer1.addRental(rental1);
customer1.addRental(rental2);
customer1.addRental(rental3);
System.out.println(customer1.statement());
- output
Rental Record for David
Madagascar 6.0
Star Wars 6.0
Gone with the Wind 11.0
Amount owed is 23.0
You earned 4 frequent renter points
Original Code - class Movie
public class Movie {
public static final int CHILDREN = 2;
public static final int REGULAR = 0;
public static final int NEW_RELEASE = 1;
private String _title;
private int _priceCode;
public Movie(String title, int priceCode) {
_title = title;
_priceCode = priceCode;
}
public int getPriceCode() { return _priceCode; }
public void setPriceCode(int arg) { _priceCode = arg; }
public String getTitle () { return _title; }
}
Original Code - class Rental
class Rental {
private Movie _movie;
private int _daysRented;
public Rental(Movie movie, int daysRented) {
_movie = movie;
_daysRented = daysRented;;
}
public int getDaysRented() { return _daysRented; }
public Movie getMovie() { return _movie; }
}
Original Code - class Customer
class Customer {
private String _name;
private Vector<Rental> _rentals = new Vector<Rental>();
public Customer (String name) { _name = name; }
public void addRental(Rental arg) { _rentals.addElement(arg); }
public String getName() { return _name; }
public String statement() {
double totalAmount = 0;
String result = "Rental Record for " + getName() + "\n";
for(Rental each: _rentals) {
double thisAmount = 0;
switch(each.getMovie().getPriceCode()){
case Moviie.REGULAR:
thisAmount += 2;
if(each.getDaysRented() > 2)
thisAmount += (each.getDaysRented() -2) * 1.5;
break;
case Movie.NEW_RELEASE:
thisAmount += each.getDaysRented() * 3;
break;
case Movie.CHILDREN:
thisAmount += 1.5;
if(each.getDaysRented() > 3)
thisAmount += (each.getDaysRented() - 3) * 1.5;
break;
}
frequentRenterPoints++;
if((each.getMovie().getPriceCode() == Movie.NEW_RELEASE)
&& each.getDaysRented() > 1)
frequentRenterPoints++;
result += "\t" + each.getMovie().getTitle() + "\t"
+ String.valueOf(thisAmount) + "\n";
totalAmount += thisAmount;
}
result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
result += "You earned " + String.valueOf(frequentRenterPoints)
+ " frequent renter points";
return result;
}
}
문제점들과 해결 방법
- 아래와 같은 상황에서 위의 코드는 OCP를 위반한다.
- 사용자가 출력 방식을 HTML형태로 출력하기를 원한다.
- 요금 규정이 변경된다.
- 새로운 영화 카테고리가 추가된다.
- 원인은 아래와 같다.
- Business Code와 UI Code가 섞여 있다.
- Business Code와 UI Code가 독립적으로 너무 자주 변경된다.
- 해결 방법
- 긴 method(statemeent())를 작은 method로 쪼갠다. 이렇게 하여 전체 method가 늘어나면 SRP를 위반할 확률이 낮아진다.
- SRP : Single ResponsibilityPrinciple, 변경의 원인은 하나여야 한다.
- 이를 위해 UI, Control, Entity Class를 나누어야 한다.
- 클래스 추가, 추상 클래스 생성을 통하여 OCP를 충족하도록 설계한다.
- 긴 method(statemeent())를 작은 method로 쪼갠다. 이렇게 하여 전체 method가 늘어나면 SRP를 위반할 확률이 낮아진다.
Step1 - UML
Statement method의 implementation을 여러 별개의 method로 extract method한다. 이 과정에서 다른 클래스의 feature를 더 많이 사용한다면 Move method 한다.
Step2 - UML
새로운 클래스를 추상 클래스 형태로 추가하여 Polymorphism을 통해 OCP를 만족시키도록 설계한다. step1에서 다른 class의 feature를 더 많이 사용하여 이동된 method를 확인할 수 있다.