코드 최적화

개발하는 운동인·2025년 10월 17일
post-thumbnail

의존성 주입 사례 1

  • OnRestUIActive에서 RestTimeQuestIonUI 클래스의 인스턴스에 접근하여 여러 Get 메서드들을 의존하고 있다.

최적화 방법 :

UI에서 모든 것을 처리 하는 사례 2

  • 아래 코드는 너무 길어졌다. 앞으로 추가할 것들이 너무 많으므로 최적화가 필요하다.
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;


public class RestTimeQuestionUI : MonoBehaviour
{
    public static string OnRestUIBacked = "OnRestUIBackEvent"; //현재 UI 활성화 된 상태에서 ESC 키 누름에 대한 이벤트

    public static string OnRestTimeCalculated = "OnRestTimeCalculateEvent";  //다음 시간까지 "최대시간"분, 최대시간 계산하는 이벤트

    public static string OnStudyTimeCalcuated = "OnStudyTimeCalcuateEvent";

    public Button GetHundredsUp() => TimeSelectUpButtons[2];
    public Button GetTensUp() => TimeSelectUpButtons[1];
    public Button GetOnesUp() => TimeSelectUpButtons[0];

    public Button GetHundredsDown() => TimeSelectDownButtons[2];
    public Button GetTensDown() => TimeSelectDownButtons[1];
    public Button GetOnesDown() => TimeSelectDownButtons[0];

    [Header("UI")]
    [SerializeField] private TextMeshProUGUI[] TimeTexts;

    [Header("버튼")]
    [SerializeField] private Button[] TimeSelectUpButtons;
    [SerializeField] private Button[] TimeSelectDownButtons;


    [SerializeField] private TextMeshProUGUI maxTimeText;

    [SerializeField]
    DailyRoutineTime dailyRoutineTime;

    private int[] digits = new int[3]; // [0]=hundreds, [1]=tens, [2]=ones

    [SerializeField]
    public int currentTime;

    [SerializeField]
    public int currentStudyTime; //공부했을 때 차감되는 시간

    [SerializeField]
    public int prevlastTime = 0;

    [SerializeField]
    int currentMaxTime = 0;

    private bool isFirstEnable = true; // 최초 1회만 GetMaxMinutes() 호출

    [SerializeField]
    private bool isTimeSelectBack;

    private void OnBackActive(bool active) 
    {
        if (active)  //ESC 누를시에 시간을 모두 0으로 초기화
        {
            digits[0] = digits[1] = digits[2] = 0;
            currentTime = 0;
            UpdateTimeUI();
        }

        isTimeSelectBack = active; 
    }

    private void OnEnable()
    {
        EventBus.Subscribe<bool>(OnRestUIBacked, OnBackActive);
        EventBus.Subscribe(OnRestTimeCalculated, SetNotFirstMaxTime);

        for(int i = 0; i < 3; i++)
        {
            int index = i; //캡쳐를 i가 아닌 index로 수행.

            TimeSelectUpButtons[2 - index ].onClick.AddListener(() => ChangeDigit(index, 1));
            TimeSelectDownButtons[2 - index].onClick.AddListener(() => ChangeDigit(index,  -1));
        }


        if (isFirstEnable)
        {
            SetFirstMaxTime();
        }
        else
        {
            Debug.Log("1");
            maxTimeText.text = $"{prevlastTime} 분";
        }


        UpdateTimeUI();
    }
    /// <summary>
    /// RestUI 활성화 되었을 때 MaxTime 계산 로직이며, 초기 MaxTime 계산임
    /// </summary>
    void SetFirstMaxTime() //초기 MaxTime
    {
        currentMaxTime = GetMaxMinutes();
        prevlastTime = currentMaxTime;
        maxTimeText.text = $"{currentMaxTime} 분";
        isFirstEnable = false;
    }
    private void OnDisable()
    {
        EventBus.UnSubscribe<bool>(OnRestUIBacked, OnBackActive);
        EventBus.UnSubscribe(OnRestTimeCalculated, SetNotFirstMaxTime);

        TimeSelectUpButtons[2].onClick.RemoveAllListeners();
        TimeSelectDownButtons[2].onClick.RemoveAllListeners();
        TimeSelectUpButtons[1].onClick.RemoveAllListeners();
        TimeSelectDownButtons[1].onClick.RemoveAllListeners();
        TimeSelectUpButtons[0].onClick.RemoveAllListeners();
        TimeSelectDownButtons[0].onClick.RemoveAllListeners();

    }
    
    /// <summary>
    /// RestUI의  MaxTime 계산 로직이며, 초기 MaxTime 계산 아님.
    /// </summary>
    void SetNotFirstMaxTime() 
    {
        prevlastTime -= currentTime - currentStudyTime; // 10 - 5 = 5

        if (prevlastTime <= 0)
        {
            EventBus.Publish(EffectManager.OnRestTimeEnded, true, currentTime);
            isFirstEnable = true;
        }

        currentTime = 0;
        digits[0] = digits[1] = digits[2] = 0;
    }

    private void ChangeDigit(int index, int delta)
    {
        int before = GetTotalMinutes();

        digits[index] += delta; //digits 배열의 인덱스에 맞게 delta값 증가

        if (delta > 0) // 자리 올림
        {
            for (int i = index; i >= 0; i--)
            {
                if (digits[i] > 9)
                {
                    digits[i] = 0;
                    if (i - 1 >= 0) digits[i - 1]++;
                }
            }
        }
        else if (delta < 0) // 자리 내림
        {
            for (int i = index; i >= 0; i--)
            {
                if (digits[i] < 0)
                {
                    digits[i] = 9;
                    if (i - 1 >= 0) digits[i - 1]--;
                }
            }
        }

        // 최소 보정
        if (GetTotalMinutes() < 0)
        {
            digits[0] = digits[1] = digits[2] = 0;
        }

        // 최대 보정 (넘으면 롤백)
        if (GetTotalMinutes() > prevlastTime) //최대 시간 초과 하면
        {
            digits[0] = before / 100;
            digits[1] = (before % 100) / 10;
            digits[2] = before % 10;
            Debug.Log("최대 시간 초과 -> 이전 값으로 세팅");
        }

        UpdateTimeUI();
    }

    private void UpdateTimeUI()
    {
        TimeTexts[2].text = digits[0].ToString();
        TimeTexts[1].text = digits[1].ToString();
        TimeTexts[0].text = digits[2].ToString();   
    }

    public int GetTotalMinutes()
    {
        currentTime = digits[0] * 100 + digits[1] * 10 + digits[2];

        return currentTime;
    }

    private int GetMaxMinutes()
    {
        DailyRoutine dailyRoutine = DailyRoutineManager.Instance.DailyRoutine;

        Debug.Log(dailyRoutine);

        switch (dailyRoutine)
        {
            case DailyRoutine.GotoSchool: return dailyRoutineTime.maxGotoSchool;
            case DailyRoutine.ArriveSchool_Breakfast: return dailyRoutineTime.maxBreakfast;
            case DailyRoutine.ArriveSchool_Morning: return dailyRoutineTime.maxMorning;
            case DailyRoutine.ArriveSchool_Lanch: return dailyRoutineTime.maxLanch;
            case DailyRoutine.ArriveSchool_Afternoon: return dailyRoutineTime.maxAfternoon;
            case DailyRoutine.Parm: return dailyRoutineTime.maxParm;
            case DailyRoutine.AfterSchool: return dailyRoutineTime.maxAfterSchool;
            default: return 0;
        }
    }
}

최적화 방법 : UI 아키텍쳐 설계

    1. RestUIManager 클래스를 만든다. 의도: 아래 설명
    1. 위 기존 클래스를 아래 의도로 할 것이다.
    1. 할당
  • 최상위 부모

  • 최상위 부모의 직속 자식의 자식

최종 계층 구조

    1. 코드 분리 및 리팩토링 시작.
  • 최상위 부모
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class RestUIManager : MonoBehaviour
{
    public static string OnRestUIBacked = "OnRestUIBackEvent"; //현재 UI 활성화 된 상태에서 ESC 키 누름에 대한 이벤트

    public static string OnRestTimeCalculated = "OnRestTimeCalculateEvent";  //다음 시간까지 "최대시간"분, 최대시간 계산하는 이벤트

    [SerializeField]
    RestTimeQuestionUI restTimeUI;

    [Header("버튼")]
    [SerializeField] public Button[] TimeSelectUpButtons;
    [SerializeField] public Button[] TimeSelectDownButtons;
    [SerializeField]
    public int currentTime;

    public Button GetHundredsUp() => TimeSelectUpButtons[2];
    public Button GetTensUp() => TimeSelectUpButtons[1];
    public Button GetOnesUp() => TimeSelectUpButtons[0];

    public Button GetHundredsDown() => TimeSelectDownButtons[2];
    public Button GetTensDown() => TimeSelectDownButtons[1];
    public Button GetOnesDown() => TimeSelectDownButtons[0];

    private void OnEnable()
    {
        for (int i = 0; i < 3; i++)
        {
            int index = i; //캡쳐를 i가 아닌 index로 수행.


            TimeSelectUpButtons[2 - index].onClick.AddListener(() => restTimeUI.ChangeDigit(index, 1));
            TimeSelectDownButtons[2 - index].onClick.AddListener(() => restTimeUI.ChangeDigit(index, -1));
        }
    }

    private void OnDisable()
    {
        for (int i = 0; i < 3; i++)
        {
            TimeSelectUpButtons[i].onClick.RemoveAllListeners();
           TimeSelectDownButtons[i].onClick.RemoveAllListeners();
        }
    }

}
  • 최상위 부모의 직속자식 클래스
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;


public class RestTimeQuestionUI : MonoBehaviour
{
    [SerializeField] private TextMeshProUGUI maxTimeText;

    [SerializeField]
    DailyRoutineTime dailyRoutineTime;

    private int[] digits = new int[3]; // [0]=hundreds, [1]=tens, [2]=ones

    [Header("텍스트")]
    [SerializeField] public TextMeshProUGUI[] TimeTexts;

    [SerializeField]
    public int currentStudyTime; //공부했을 때 차감되는 시간

    [SerializeField]
    public int prevlastTime = 0;

    [SerializeField]
    int currentMaxTime = 0;

    private bool isFirstEnable = true; // 최초 1회만 GetMaxMinutes() 호출

    [SerializeField]
    private bool isTimeSelectBack;

    [SerializeField]
    RestUIManager restUIManager;


    public void OnBackActive(bool active)
    {
        if (active)  //ESC 누를시에 시간을 모두 0으로 초기화
        {
            digits[0] = digits[1] = digits[2] = 0;
            restUIManager.currentTime = 0;
            UpdateTimeUI();
        }

        isTimeSelectBack = active;
    }

    private void OnEnable()
    {
        EventBus.Subscribe<bool>(RestUIManager.OnRestUIBacked, OnBackActive);
        EventBus.Subscribe(RestUIManager.OnRestTimeCalculated, SetNotFirstMaxTime);

        if (isFirstEnable)
        {
            SetFirstMaxTime();
        }
        else
        {
            Debug.Log("1");
            maxTimeText.text = $"{prevlastTime} 분";
        }


        UpdateTimeUI();
    }
    /// <summary>
    /// RestUI 활성화 되었을 때 MaxTime 계산 로직이며, 초기 MaxTime 계산임
    /// </summary>
    public void SetFirstMaxTime() //초기 MaxTime
    {
        currentMaxTime = GetMaxMinutes();
        prevlastTime = currentMaxTime;
        maxTimeText.text = $"{currentMaxTime} 분";
        isFirstEnable = false;
    }
    private void OnDisable()
    {
        EventBus.UnSubscribe<bool>(RestUIManager.OnRestUIBacked, OnBackActive);
        EventBus.UnSubscribe(RestUIManager.OnRestTimeCalculated, SetNotFirstMaxTime);
    }

    /// <summary>
    /// RestUI의  MaxTime 계산 로직이며, 초기 MaxTime 계산 아님.
    /// </summary>
    void SetNotFirstMaxTime()
    {
        prevlastTime -= restUIManager.currentTime - currentStudyTime; // 10 - 5 = 5

        if (prevlastTime <= 0)
        {
            EventBus.Publish(EffectManager.OnRestTimeEnded, true, restUIManager.currentTime);
            isFirstEnable = true;
        }

        restUIManager.currentTime = 0;
        digits[0] = digits[1] = digits[2] = 0;
    }

    public void ChangeDigit(int index, int delta)
    {
        int before = GetTotalMinutes();

        digits[index] += delta; //digits 배열의 인덱스에 맞게 delta값 증가

        if (delta > 0) // 자리 올림
        {
            for (int i = index; i >= 0; i--)
            {
                if (digits[i] > 9)
                {
                    digits[i] = 0;
                    if (i - 1 >= 0) digits[i - 1]++;
                }
            }
        }
        else if (delta < 0) // 자리 내림
        {
            for (int i = index; i >= 0; i--)
            {
                if (digits[i] < 0)
                {
                    digits[i] = 9;
                    if (i - 1 >= 0) digits[i - 1]--;
                }
            }
        }

        // 최소 보정
        if (GetTotalMinutes() < 0)
        {
            digits[0] = digits[1] = digits[2] = 0;
        }

        // 최대 보정 (넘으면 롤백)
        if (GetTotalMinutes() > prevlastTime) //최대 시간 초과 하면
        {
            digits[0] = before / 100;
            digits[1] = (before % 100) / 10;
            digits[2] = before % 10;
            Debug.Log("최대 시간 초과 -> 이전 값으로 세팅");
        }

        UpdateTimeUI();
    }

    private void UpdateTimeUI()
    {
        TimeTexts[2].text = digits[0].ToString();
        TimeTexts[1].text = digits[1].ToString();
        TimeTexts[0].text = digits[2].ToString();
    }

    public int GetTotalMinutes()
    {
        restUIManager.currentTime = digits[0] * 100 + digits[1] * 10 + digits[2];

        return restUIManager.currentTime;
    }

    private int GetMaxMinutes()
    {
        DailyRoutine dailyRoutine = DailyRoutineManager.Instance.DailyRoutine;

        Debug.Log(dailyRoutine);

        switch (dailyRoutine)
        {
            case DailyRoutine.GotoSchool: return dailyRoutineTime.maxGotoSchool;
            case DailyRoutine.ArriveSchool_Breakfast: return dailyRoutineTime.maxBreakfast;
            case DailyRoutine.ArriveSchool_Morning: return dailyRoutineTime.maxMorning;
            case DailyRoutine.ArriveSchool_Lanch: return dailyRoutineTime.maxLanch;
            case DailyRoutine.ArriveSchool_Afternoon: return dailyRoutineTime.maxAfternoon;
            case DailyRoutine.Parm: return dailyRoutineTime.maxParm;
            case DailyRoutine.AfterSchool: return dailyRoutineTime.maxAfterSchool;
            default: return 0;
        }
    }
}

0개의 댓글