ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [객체 지향 프로그래밍]
    자유게시판 2024. 11. 10. 20:08
    • 리팩토링Tell Dont Ask(TDA) 원칙
      • TDA란객체의 상태나 데이터를 외부에서 직접 요청하거나 변경하지 않고, 객체에 메소드를 호출하여 그 객체가 스스로 책임지고 행동하도록 해야 한다는 원칙이다.
        class BankAccount {
            private int balance;
        
            public int getBalance() {
                return balance;
            }
        
            public void setBalance(int amount) {
                balance = amount;
            }
        }
        
        class AccountManager {
            public void processAccount(BankAccount account) {
                if (account.getBalance() > 1000) {
                    // 높은 잔액을 처리하는 로직
                } else {
                    // 잔액이 적을 때의 처리
                }
            }
        }
        
        
        위 코드에서는 AccountManager 가 BankAccount 의 잔액을 확인하고, 그 값을 기반으로 로직을 처리하고 있습니다. TDA의 “asking” 방식
        class BankAccount {
            private int balance;
        
            public void processTransaction() {
                if (balance > 1000) {
                    // 잔액이 많을 경우 처리
                } else {
                    // 잔액이 적을 경우 처리
                }
            }
        }
        
        class AccountManager {
            public void processAccount(BankAccount account) {
                account.processTransaction();
            }
        }
        
        
        • 이 코드에서는 AccountManager가 BankAccount에 대해 상태를 직접 확인하지 않고, BankAccount 객체가 자체적으로 상태를 관리하며 로직을 처리하도록 했습니다. 이는 "telling" 방식입니다.
        장점
        • 캡슐화: 객체가 자신을 어떻게 처리할지 책임지게 하여, 외부에서 객체의 내부 상태에 접근하는 것을 줄일 수 있다. 쉽게 말해 “나쁜 예” 에서 내부( account.getBalance )에서 로직을 바꾸면 processAccount 로직도 변경해야 할 수도 있다. 반대의 경우도 마찬가지이다. 외부에서에 account,getBalance() 에 접근해 값을 변경하는 경우 여기서는 딱 보이지만, 스케일이 커질 때, 모든 코드를 하나씩 확인해서 처리하는 건 매우 힘들고 어려울 것이다.
      • [좋은 예(Tell)]
      • [나쁜 예(ASK)]
      • 객체 지향 설계에서 중요한 개념 중 하나.
      [devices/timer.c][threads/thread.c]
      • 현재 구현에서 TDA 원칙을 위반하는 부분은 timer_interrupt 함수에서 get_next_tick_to_awake() 를 호출하여 외부에서 next_tick_to_awake 값을 직접 가져오는 부분이다. 이는 timer_interrupt 가 next_tick_to_awake 를 직접 확인하는 방식으로 TDA 원칙에서 말하는 “asking”에 해당한다.
      • 이 문제를 해결하려면 next_tick_to_awake 값을 외부에서 가져오는 대신, 해당 객체나 구조체(여기서는 thread 나 timer 관련 객체)에게 상태를 확인하도록 “tell” 방식으로 설계해야 한다. 즉, 객체가 스스로 언제 깨어날지 결정하고 그에 따라 행동하도록 해야 한다.
      [해결방법]
      • thread 나 timer 가 “스스로 다음 깨어날 시간을 확인하고, 그에 따라 행동하게” 만들어야 한다.”
        • timer_interrupt에서 직접 next_tick_to_awake 값을 조회하는 대신, thread 나 timer 객체에 “다음 깨어날 시간을 결정”하도록 요청하는 방식으로 변경해야 한다.
      • thread 객체에서 next_tick_to_awake 를 관리하도록 리팩토링
        • thread 나 timer 가 스스로 다음 깨어날 시간을 계산하고, timer_interrupt 에서는 그 시간에 맞춰 작업을 수행하도록 할 수 있다.
      /* Timer interrupt handler */
      static void
      timer_interrupt (struct intr_frame *args UNUSED) {
          ticks++;
          thread_tick ();
      
          /* project1-Alarm Clock */
          thread_check_awake(ticks);  // next_tick_to_awake와 관련된 로직을 thread가 처리하도록 위임
      }
      
      /* thread.c */
      void thread_check_awake(int64_t current_ticks) {
          if (next_tick_to_awake <= current_ticks) {
              thread_awake(current_ticks);
          }
      }
      
      • thread에 get_next_tick_to_awake 를 요청하지 않고, thread 가 스스로 처리하도록 수정
      /* thread.c */
      void 
      thread_awake (int64_t wakeup_tick) 
      {
          next_tick_to_awake = INT64_MAX;
      
          struct list_elem *sleeping;
          sleeping = list_begin(&sleep_list);  
      
          while (sleeping != list_end(&sleep_list)) {  
              struct thread *th = list_entry(sleeping, struct thread, elem);
      
              if (wakeup_tick >= th->wakeup_tick) 
      		{
                  sleeping = list_remove(&th->elem);  
                  thread_unblock(th);                 
              } 
      		else 
      		{
                  sleeping = list_next(sleeping);             
                  update_next_tick_to_awake(th->wakeup_tick);  
              }
          }
      }
      
      /* thread.c */
      /* threads/thread.c */
      void thread_check_awake(int64_t current_ticks) {
          if (next_tick_to_awake <= current_ticks) {
              thread_awake(current_ticks);
          }
      }
      
      
      • thread_awke 함수가 next_tick_to_awake 값을 업데이트하거나, thread 객체가 스스로 다음 깨어날 시간을 계산하도록 설계할 수 있다.
    • /* project1-Alarm Clock */ int64_t get_next_tick_to_awake(void) { return next_tick_to_awake; }
    • /* Timer interrupt handler. */ static void timer_interrupt (struct intr_frame *args UNUSED) { ticks++; thread_tick (); /* project1-Alarm Clock */ if (get_next_tick_to_awake() <= ticks) { thread_awake(ticks); } }
    • 물어보지 말고 시켜라.
Designed by Tistory.