메서드 대부분이 공통된 인스턴스 변수를 사용할수록 응집도가 높다.
public class Stack { private int _topOfStack = 0; private List<int> _elements = new List<int>(); public int Size() => _topOfStack; public void Push(int element) { _topOfStack++; _elements.Add(element); } public int Pop() { if (_topOfStack == 0) throw new InvalidOperationException("Stack is empty."); int element = _elements[--_topOfStack]; _elements.RemoveAt(_topOfStack); return element; } }
Size(), Push(), Pop() 모두 _topOfStack과 _elements를 함께 사용한다.
클래스가 오직 '스택'이라는 하나의 개념을 중심으로 구성되어 있다.
인스턴스 변수가 혼자 노는 경우가 없다. 책임이 명확하다.
큰 함수에서 일부를 별도 함수로 추출하려는데, 그 부분이 지역 변수를 4개 사용한다. 이때 그 변수들을 클래스 인스턴스 변수로 승격하면…
SRP를 지키면 변경이 필요한 지점에만 수정이 국소화된다.
public class Sql { public Sql(String table, Column[] columns) public String create() public String insert(Object[] fields) public String selectAll() public String findByKey(String keyColumn, String keyValue) public String select(Column column, String pattern) public String preparedInsert() // private 메서드들... }
SRP 위반: SQL 생성과 관련된 모든 책임이 하나의 클래스에 있다.
변경 이유 다수: CREATE 변경, INSERT 변경, SELECT 변경 — 각기 다른 이유로 이 클래스를 수정하게 된다.
강한 결합: insert(), select(), findByKey()는 다른 동작인데 같은 클래스에 묶여 있다.
// STEP 1: abstract base class public abstract class Sql { protected readonly string Table; protected readonly Column[] Columns; protected Sql(string table, Column[] columns) { Table = table; Columns = columns; } public abstract string Generate(); }
// STEP 2: 역할별 구체 클래스 public class CreateSql : Sql { public CreateSql(string table, Column[] columns) : base(table, columns) { } public override string Generate() { var cols = string.Join(", ", Columns.Select(c => $"{c.Name} {c.Type}")); return $"CREATE TABLE {Table} ({cols});"; } } public class InsertSql : Sql { private readonly object[] _values; public InsertSql(string table, Column[] columns, object[] values) : base(table, columns) { _values = values; } public override string Generate() { var columnList = string.Join(", ", Columns.Select(c => c.Name)); var valueList = string.Join(", ", _values.Select(v => $"'{v}'")); return $"INSERT INTO {Table} ({columnList}) VALUES ({valueList});"; } } // SelectSql, FindByKeySql 등도 동일하게...
응집도 높은 클래스 설계와 변경에 유연한 구조
메서드 대부분이 같은 인스턴스 변수를 사용. 하나의 목적으로 구성.
일부만 쓰는 변수가 생기면 독립 클래스로 분리하라.
기존 클래스를 수정하지 않고 새 클래스로 확장 가능하게 (OCP).
클래스는 오직 하나의 이유로만 변경되어야 한다.