소프트웨어 개발에 있어서 중요한 '데이터 불변성'의 개념
데이터가 생성된 후에는 데이터가 변경되는 것에는 신중을 가해야한다.
변수가 변경안되게 막아볼라고 애쓰는 리팩토링의 일부 예시들
class User {
public string Name { get; set; }
public void UpdateName(string newName) {
Name = newName;
}
}
class User {
// Name 속성을 private setter를 사용하여 불변성을 유지함.
public string Name { get; private set; }
public User(string name) {
Name = name;
}
// 이름을 굳이 변경하겠다면, 이 메소드를 통해 새로운 user 인스턴스를 생성해야함.
// 이렇게 하면 데이터의 불변성을 유지하는 원칙을 지키면서도, 필요에 따라 변경할 수 있는 유연성을 주는 것.
public User WithUpdatedName(string newName) {
return new User(newName);
}
}
var user = new User("Bob");
var updatedUser = user.WithUpdatedName("Alice");
객체 지향 프로그래밍의 중요한 원칙 : 단일 책임 원칙 (Single Responsibilty Principle, SRP)
하나의 클래스는 하나의 기능만 한다.
한 클래스에 여러 기능이 집중되어 있어서 하나의 변경이 여러 부분에 영향을 미칠 때 발생한다.
샷건 수술(Shotgun Surgery) : 변경이 필요할 때 여러 다른 클래스를 수정해야 하는 상황
// 이 클래스는 금융상품 관리, 데이터 베이스 연결, 데이터 가져오기 혼자 다 해 먹음
class FinancialProductManager {
public void AddFinancialProduct() { /*...*/ }
public void RemoveFinancialProduct() { /*...*/ }
public void ConnectToDatabase() { /*...*/ }
public void FetchDataFromDatabase() { /*...*/ }
}
// 금융 상품 관리와 데이터베이스 관리를 분리함.
// 각 클래스는 자신의 책임에만 집중한다.
class FinancialProductManager {
public void AddFinancialProduct() { /*...*/ }
public void RemoveFinancialProduct() { /*...*/ }
}
class DatabaseManager {
public void Connect() { /*...*/ }
public void FetchData() { /*...*/ }
}
복잡한 데이터를 단순한 기본형(int, string)에 과도하게 의존하는 경향
복잡한 개념은 기본형 대신 클래스나, 구조체를 사용해서 차라리 해결하자.
예) 전화번호나 화폐 단위는 사실 단순 문자열이나 숫자에서 끝나지 않고, 여러 규칙과 로직을 포함할 수 있다.
// 화폐단위랑, 전화번호를 그냥 변수로만 처리하고 끝냄
class Order {
public string Currency; // 예: "USD", "EUR"
public string PhoneNumber; // 예: "+1-123-456-7890"
// 다른 메소드들...
}
// 화폐랑 전화번호를 별도의 클래스로 만들고, 클래스 내에서 각자 로직과 유효성 검사를 추가함.
class Currency {
private string Code; // 예: "USD", "EUR"
public Currency(string code) {
// 유효성 검사...
Code = code;
}
// 관련 메소드들...
}
class PhoneNumber {
private string Number; // 예: "+1-123-456-7890"
public PhoneNumber(string number) {
// 유효성 검사...
Number = number;
}
// 관련 메소드들...
}
class Order {
public Currency Currency;
public PhoneNumber PhoneNumber;
// 다른 메소드들...
}
switch, if문이 코드에 중복되는 문제
switch문은 사실 협업에서 보기 힘듬 (코드 리뷰시 백퍼센트 까이는 구조)
객체지향 프로그래밍의 다형성을 활용하도록 하자.
각 조건에 따른 행동을 별도의 클래스로 정의하고, 이걸 인터페이스나 추상 클래스로 다루는게 멋진 코드.
public enum AnimalType {
Dog,
Cat,
Bird
}
public class Animal {
public AnimalType Type { get; set; }
public string MakeSound() {
switch (Type) {
case AnimalType.Dog:
return "Bark";
case AnimalType.Cat:
return "Meow";
case AnimalType.Bird:
return "Tweet";
default:
throw new NotImplementedException();
}
}
}
public interface IAnimal {
string MakeSound();
}
public class Dog : IAnimal {
public string MakeSound() {
return "Bark";
}
}
public class Cat : IAnimal {
public string MakeSound() {
return "Meow";
}
}
public class Bird : IAnimal {
public string MakeSound() {
return "Tweet";
}
}
너무 오바하지 말자.
필요하지 않는 클래스, 메소드, 인터페이스는 그때 그때 삭제해주거나, 인라인화를 하자.
class UnnecessaryClass {
public int Add(int a, int b) {
return a + b;
}
}
class Calculator {
private UnnecessaryClass helper = new UnnecessaryClass();
public int CalculateSum(int x, int y) {
return helper.Add(x, y);
}
}
class Calculator {
public int CalculateSum(int x, int y) {
return x + y;
}
}