object-oriented-programmer skill
객체 지향 및 멀티 패러다임 코드베이스의 복잡성을 관리하기 위한 객체 지향 설계 원칙, 패턴 및 실무 가이드를 제공합니다.
- GitHub 스타
- 9
- 파일
- 1
- 마지막 커밋
- 2026년 10월 13일
- 라이선스
- EPL-2.0
- 허용된 도구
- —
object-oriented-programmer skill은 무엇을 하나요?
object-oriented-programmer은(는) agent skill입니다. 객체 지향 및 멀티 패러다임 코드베이스의 복잡성을 관리하기 위한 객체 지향 설계 원칙, 패턴 및 실무 가이드를 제공합니다.
skill의 SKILL.md에서 생성된 요약입니다.
object-oriented-programmer skill 정보
이 SKILL은 객체 지향 또는 멀티 패러다임 환경에서 작업하는 개발자가 복잡한 상태와 동작을 가진 시스템을 설계할 수 있도록 지원합니다. 캡슐화, 상속, 다형성을 통해 복잡성을 관리할 수 있는 기초적인 프레임워크를 제공합니다. Java, C#, C++, Python, Ruby, Swift와 같은 언어를 다룰 때, 특히 특정 언어 전용 SKILL을 사용할 수 없거나 광범위한 코드베이스에 객체 지향 원칙을 적용해야 하는 경우 에이전트가 이 SKILL을 로드해야 합니다.
이 SKILL은 단일 책임 원칙과 리스코프 치환 원칙을 포함하여 SOLID 휴리스틱과 같은 기본 원칙에 대한 가이드를 생성합니다. 또한 Strategy, Observer, Builder 패턴과 같은 생성, 구조, 행위 디자인 패턴을 구현하는 방법에 대한 실질적인 조언을 제공합니다. 추상화와 의존성 역전에 집중함으로써, 개발자가 과도한 엔지니어링이나 과도한 상속으로 인한 강한 결합과 같은 일반적인 함정을 피하면서 확장 가능한 프레임워크를 구축하고 풍부한 엔티티 관계를 모델링할 수 있도록 돕습니다.
agent가 수행할 수 있는 작업
- 1SOLID 설계 원칙 적용에이전트에게 클래스를 평가하여 단일 책임 원칙을 준수하는지 확인하도록 요청합니다.
- 2디자인 패턴 구현런타임에 교체 가능한 알고리즘을 처리하기 위한 패턴 추천을 요청합니다.
- 3상속을 합성으로 리팩터링경직된 상속 계층 구조를 더 유연한 합성 기반 방식으로 교체하는 방법에 대한 가이드를 요청합니다.
- 4복잡한 도메인 엔티티 모델링복잡한 관계를 가진 실제 객체를 나타내는 코드 구조를 설계할 때 이 SKILL을 사용합니다.
skill의 SKILL.md를 기반으로 작성되었습니다. 모든 사용 사례는 해당 문서의 인용구와 대조 확인되었습니다.
object-oriented-programmer skill 설치 방법
각 명령은 skill 폴더를 클라이언트의 skills 디렉터리에 복사합니다. 클라이언트를 재시작하거나 새 세션을 시작하여 로드하세요.플러그인에 object-oriented-programmer skill 설치
/plugin marketplace add Pyroxin/opinionated-claude-skills /plugin install opinionated-software-engineering@opinionated-claude-skills
Claude Code에 object-oriented-programmer skill 설치
~/.claude/skills/object-oriented-programmer/SKILL.md
$ npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/object-oriented-programmer ~/.claude/skills/object-oriented-programmerClaude Code (project)에 object-oriented-programmer skill 설치
.claude/skills/object-oriented-programmer/SKILL.md
$ npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/object-oriented-programmer .claude/skills/object-oriented-programmerCodex에 object-oriented-programmer skill 설치
~/.agents/skills/object-oriented-programmer/SKILL.md
$ npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/object-oriented-programmer ~/.agents/skills/object-oriented-programmerGemini CLI에 object-oriented-programmer skill 설치
~/.gemini/skills/object-oriented-programmer/SKILL.md
$ npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/object-oriented-programmer ~/.gemini/skills/object-oriented-programmerClaude.ai에 object-oriented-programmer skill 설치
npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/object-oriented-programmer object-oriented-programmer && zip -r object-oriented-programmer.zip object-oriented-programmer # claude.ai → Customize → Skills → + → Create skill → Upload a skill → object-oriented-programmer.zip
SKILL.md
GitHub에서 보기Object-Oriented Programmer
Purpose
This skill provides guidance on object-oriented programming principles, design patterns, and practices. Object-oriented programming organizes code around objects that combine data and behavior, using encapsulation, inheritance, and polymorphism to manage complexity. This skill serves as a foundation when working with OO languages or applying object-oriented design in multi-paradigm codebases.
When to Use This Skill
Use this skill when:
- Working with object-oriented languages (Java, C#, C++, Python, Ruby, Swift) without language-specific skills available
- Designing systems with complex state and behavior
- Modeling domains with rich entity relationships
- Building frameworks or libraries with extension points
- Working with existing OOP codebases
Note: Language-specific skills (e.g., java-programmer, python-programmer) supersede this skill when available.
Core Philosophy
Objects as Encapsulated State and Behavior
Object-oriented programming bundles data (state) with the operations (behavior) that act on that data. An object presents a clean interface while hiding implementation details. This encapsulation creates boundaries that limit coupling and enable local reasoning.
Quote to remember: "Bad programmers worry about the code. Good programmers worry about data structures and their relationships." — Linus Torvalds
Polymorphism Enables Abstraction
Polymorphism allows treating different types uniformly through shared interfaces. This enables writing code against abstractions rather than concrete types, making systems more flexible and extensible.
Types of polymorphism:
- Subtype polymorphism — Inheritance hierarchies (interfaces, abstract classes)
- Parametric polymorphism — Generics/templates
- Ad-hoc polymorphism — Method overloading
Inheritance as Code Reuse (Use Carefully)
Inheritance enables defining types in terms of other types, inheriting both interface and implementation. However, inheritance creates tight coupling between parent and child classes.
The inheritance trade-off: Inheritance is easy to add (create subclass) but hard to change (affects all subclasses). Composition is harder to add (requires more boilerplate) but easier to change (localized impact).
Modern wisdom: "Composition over inheritance" — prefer delegating to contained objects over inheriting from parent classes.
Fundamental Principles
Encapsulation Hides Complexity
Encapsulation bundles related data and behavior while hiding internal implementation. Objects expose interfaces; internals are private.
Why encapsulation matters:
- Limits coupling (changes don't ripple through codebase)
- Enables local reasoning (understand object in isolation)
- Protects invariants (object controls its own consistency)
- Provides flexibility (change internals without affecting clients)
Encapsulation boundaries:
- Private state, public interface
- Package-private for internal APIs
- Protected for inheritance hierarchies (use sparingly)
Abstraction Manages Complexity
Abstraction focuses on essential characteristics while hiding incidental details. Interfaces and abstract classes define contracts without specifying implementation.
Why abstraction matters:
- Program against interfaces, not implementations
- Enable substitution (Liskov Substitution Principle)
- Defer decisions (choose implementations later)
- Facilitate testing (mock interfaces easily)
Abstraction levels:
- Interfaces — Pure contracts (no implementation)
- Abstract classes — Partial implementation with extension points
- Concrete classes — Full implementation
Polymorphism Enables Flexibility
Polymorphism allows uniform treatment of different types. Write code once that works with many implementations.
Why polymorphism matters:
- Add new implementations without changing client code
- Strategy pattern (choose behavior at runtime)
- Plugin architectures (extend without modifying core)
- Testing (inject mocks/stubs through interfaces)
Polymorphism trade-off: Indirection obscures flow. Reading polymorphic code requires knowing what implementations exist and which is active. Balance flexibility against clarity.
<solid_principles>
SOLID Principles
SOLID provides guidelines for object-oriented design. These are heuristics, not laws—apply them where they improve code, not dogmatically.
<single_responsibility>
Single Responsibility Principle
A class should have one reason to change. Each class should do one thing well.
Good indicators:
- Can describe class purpose in one sentence without "and"
- Changes to requirements affect only one class
- Class has cohesive set of methods
When to violate:
- Very small classes (splitting increases complexity)
- Clearly related concerns (don't separate prematurely)
- Performance-critical hot paths (fewer objects, fewer allocations)
Common mistake: Confusing "single responsibility" with "one method." Classes can have multiple methods serving one coherent purpose. </single_responsibility>
<open_closed>
Open-Closed Principle
Open for extension, closed for modification. Add new behavior without changing existing code.
Implementation strategies:
- Inheritance — Extend base classes (classic OCP)
- Composition — Inject dependencies (modern preference)
- Strategy pattern — Plug in different algorithms
- Template method — Override specific steps
When to violate:
- Requirements fundamentally change (refactor rather than extend)
- Abstraction is speculative (YAGNI — You Aren't Gonna Need It)
- System is small and changes are cheap
Staff insight: OCP assumes future requirements. Don't add extension points for hypothetical needs. Wait until you need to extend, then refactor to enable it. </open_closed>
<liskov_substitution>
Liskov Substitution Principle
Subtypes must be substitutable for their base types.1 Derived classes should strengthen (not weaken) base class contracts.
What LSP means:
- Preconditions cannot be strengthened (subclass can't be more restrictive)
- Postconditions cannot be weakened (subclass must do at least as much)
- Invariants must be preserved (subclass maintains base class guarantees)
Common LSP violations:
- Square extending Rectangle (breaks area calculation expectations)
- ReadOnlyCollection extending Collection (throws on mutating methods)
- Overriding methods to do nothing or throw exceptions
When inheritance violates LSP, use composition instead.
<interface_segregation>
Interface Segregation Principle
Clients shouldn't depend on interfaces they don't use. Many specific interfaces beat one general interface.
Why ISP matters:
- Smaller surface area (easier to understand)
- Avoid implementing unnecessary methods
- Reduce coupling (changes affect fewer clients)
- Enable role-based interfaces
When to violate:
- Interface has few methods (splitting is overhead)
- All clients use all methods
- Language lacks interface composition (can't extend multiple interfaces)
Staff insight: ISP fights "fat interfaces." If clients only use subset of methods, split the interface. But don't create explosion of single-method interfaces either. </interface_segregation>
<dependency_inversion>
Dependency Inversion Principle
Depend on abstractions, not concretions. High-level modules shouldn't depend on low-level modules.
Implementation:
- Inject dependencies rather than constructing them
- Program against interfaces, not classes
- Use dependency injection frameworks (Spring, Guice) or manual constructor injection
Why DIP matters:
- Testability (inject mocks)
- Flexibility (swap implementations)
- Parallel development (implement against interfaces)
- Inversion of control (framework calls you)
When to violate:
- Value objects and data structures (no abstraction needed)
- Stable dependencies (standard library, mature frameworks)
- Performance-critical paths (virtual dispatch has cost) </dependency_inversion> </solid_principles>
Design Patterns: Use With Judgment
The Gang of Four patterns are valuable but often overused. Apply patterns to solve real problems, not to demonstrate pattern knowledge.
Quote to remember: "Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it." — Alan Perlis
Creational Patterns
Factory Method — Delegate object creation to subclasses
- Use when: Creation logic varies by subtype
- Avoid when: Simple construction suffices
Abstract Factory — Create families of related objects
- Use when: Multiple related object types must be consistent
- Avoid when: Only one object type or no consistency requirements
Builder — Construct complex objects step-by-step
- Use when: Many optional parameters or complex construction
- Avoid when: Simple constructors suffice
Singleton — Ensure single instance exists
- Use when: Global state is truly necessary (logging, config)
- Avoid when: Global state can be avoided (usually)
- Staff insight: Singletons are global state. Prefer dependency injection.
Structural Patterns
Adapter — Convert one interface to another
- Use when: Integrating incompatible interfaces
- Good for: Wrapping third-party libraries
Decorator — Add behavior without subclassing
- Use when: Adding responsibilities dynamically
- Good for: Cross-cutting concerns (logging, caching)
Facade — Simplify complex subsystems
- Use when: Complex system needs simple interface
- Good for: API boundaries, legacy system integration
Composite — Treat individuals and compositions uniformly
- Use when: Tree structures (UI hierarchies, file systems)
- Pattern: Same interface for leaf and composite
Behavioral Patterns
Strategy — Encapsulate interchangeable algorithms
- Use when: Multiple algorithms, runtime selection
- Good for: Different sorting, validation, pricing strategies
Observer — Notify dependents of state changes
- Use when: One-to-many dependencies, event handling
- Watch for: Memory leaks (observers not released)
Template Method — Define algorithm skeleton, defer steps to subclasses
- Use when: Algorithm structure is fixed, steps vary
- Alternative: Strategy pattern with composition
Command — Encapsulate requests as objects
- Use when: Queuing operations, undo/redo, logging
- Good for: Job queues, macro recording
Visitor — Separate algorithm from object structure
- Use when: Operations vary often, structure rarely changes
- Dual to: Functional approach (pattern matching)
Pattern Pitfalls
Avoid pattern-driven design:
- Don't apply patterns because you "should"
- Don't choose patterns before understanding the problem
- Don't use patterns to demonstrate sophistication
Watch for over-engineering:
- Simple problems don't need complex patterns
- Patterns add indirection (cognitive cost)
- Future flexibility has present complexity cost
Quote to remember: "The cheapest, fastest, and most reliable components are those that aren't there." — Gordon Bell
Staff-Level Insights
When OOP Works Well
Object-oriented programming excels in specific contexts:
Domain modeling with rich behavior:
- Entities with complex state and invariants
- Business logic tied to data
- Systems modeling real-world objects
Frameworks and extensibility:
- Plugin architectures
- Template methods for customization
- Inversion of control containers
UI and stateful systems:
- GUI widgets with state and event handlers
- Game entities with behavior
- Simulations with evolving state
Legacy codebases:
- Established OOP systems (don't fight the paradigm)
- Team expertise in OOP patterns
- Ecosystem built around OOP (frameworks, libraries)
When OOP Struggles
Data transformation pipelines:
- FP map/filter/reduce is clearer
- Immutable transformations avoid state bugs
- Composition over object hierarchies
Highly concurrent systems:
- Shared mutable state causes race conditions
- Message passing (Erlang/Elixir) or immutability (Clojure) may be better
- Actor model or functional approaches reduce concurrency issues
Simple utilities and scripts:
- Functions suffice without object structure
- Overhead of classes doesn't pay off
- Procedural or functional style is clearer
Systems requiring frequent new operations:
- Expression problem: OOP makes adding operations hard
- Every new operation requires touching all classes
- Functional approach (pattern matching) may be easier
The Expression Problem Revisited
The expression problem2 describes a fundamental trade-off between extensibility dimensions:
Object-oriented trade-off:
- Easy to add new types (create new subclass)
- Hard to add new operations (modify all classes)
When this trade-off favors OOP:
- Domain has stable set of operations
- New types are added frequently
- Example: UI widgets (standard operations: render, handle input; many widget types)
When this trade-off hurts OOP:
- Operations change frequently
- Types are relatively stable
- Example: Compiler (fixed AST nodes; many new optimization passes)
Solutions:
- Visitor pattern (add operations to OO systems)
- Multi-methods (CLOS, Clojure)
- Type classes (Haskell)
- Accept the trade-off based on domain
Inheritance vs. Composition
When inheritance works:
- True "is-a" relationship (not just code reuse)
- Liskov Substitution holds
- Subclass extends, not replaces, parent behavior
- Hierarchy is shallow (2-3 levels max)
When composition is better:
- "Has-a" or "uses-a" relationship
- Behavior can be mixed and matched
- Runtime flexibility needed
- Deep hierarchies forming
Staff insight: Default to composition. Only use inheritance when substitutability is genuinely needed. Most "inheritance for code reuse" is better served by composition or helper functions.
Quote to remember: "Favor composition over inheritance." — Gang of Four, Design Patterns
Abstraction Costs
Abstraction has benefits but also costs:
Benefits:
- Flexibility (swap implementations)
- Testability (inject mocks)
- Separation of concerns
Costs:
- Indirection (harder to trace execution)
- Cognitive load (understand interface + implementations)
- Performance (virtual dispatch overhead)
Guidelines:
- Abstract when you have multiple implementations or expect future ones
- Abstract at module boundaries (not within modules)
- Don't abstract prematurely (YAGNI)
- Measure abstraction cost (some indirection is fine; seven layers is not)
Testing Object-Oriented Code
OOP enables testing through:
- Dependency injection (inject mocks/stubs)
- Interface-based design (implement test doubles)
- Encapsulation (test through public API)
OOP testing challenges:
- Deep inheritance hierarchies (hard to set up state)
- Tight coupling (can't test in isolation)
- Hidden dependencies (static methods, singletons)
Testing guidelines:
- Favor composition (easier to inject test doubles)
- Minimize statics and singletons (global state)
- Test through interfaces (implementation agnostic)
- Keep setup simple (complex setup indicates design issues)
OOP in Multi-Paradigm Languages
Many modern languages support multiple paradigms (Python, JavaScript, Ruby, Swift):
Applying OOP selectively:
- Use objects for stateful entities
- Use functions for transformations
- Use modules/namespaces for organization
- Mix paradigms within same codebase
Don't force OOP everywhere:
- Not every function needs a class
- Utility functions can stand alone
- Modules organize without inheritance
- Accept procedural or functional code where clearer
Staff insight: OOP is one tool. Modern software engineering embraces multi-paradigm thinking. Choose the right paradigm for each problem rather than forcing everything into objects.
<anti_patterns>
Common Pitfalls and Anti-Patterns
<god_objects>
God Objects
Anti-pattern: One class does too much, knows too much, controls too much.
Fix:
- Split responsibilities into focused classes
- Apply Single Responsibility Principle
- Create collaborating objects instead of one omniscient object </god_objects>
<anemic_domain_model>
Anemic Domain Model
Anti-pattern: Objects with only getters/setters, no behavior. Logic lives in separate "service" classes.3
When it's actually fine:
- Data transfer objects (DTOs)
- Value objects
- Database entities in some architectures
When it's a problem:
- Domain logic is scattered across services
- Objects are just data bags with no encapsulation
Fix: Move behavior to objects that own the data
<deep_inheritance>
Deep Inheritance Hierarchies
Anti-pattern: 5+ levels of inheritance creating brittle, hard-to-understand hierarchies.
Fix:
- Flatten hierarchy using composition
- Interfaces instead of abstract base classes
- Keep hierarchies shallow (2-3 levels max) </deep_inheritance>
<primitive_obsession>
Primitive Obsession
Anti-pattern: Using primitives instead of small objects (string for email, int for money).
Fix:
- Create value objects (Email, Money classes)
- Encapsulate validation and behavior
- Type safety through distinct types </primitive_obsession>
<feature_envy>
Feature Envy
Anti-pattern: Method in one class uses data/methods from another class more than its own.
Fix:
- Move method to the class it envies
- Create new class if both classes envy common data
- Apply "Tell, Don't Ask" principle </feature_envy>
<inappropriate_intimacy>
Inappropriate Intimacy
Anti-pattern: Classes know too much about each other's internals.
Fix:
- Increase encapsulation
- Communicate through well-defined interfaces
- Reduce coupling between classes </inappropriate_intimacy> </anti_patterns>
Common Mistakes from Functional Programming Backgrounds
Programmers coming from functional languages sometimes resist OOP patterns that would actually clarify:
Avoiding necessary state when mutation is clearer:
- Mistake: Immutable everything, even for inherently stateful domains (UI widgets, game entities)
- Fix: Embrace encapsulated mutable state. OOP's strength is managing state safely through encapsulation
Creating anemic domain models:
- Mistake: Separating data from behavior (DTOs + service layers) because "data should be separate"
- Fix: Co-locate data with operations that maintain invariants. Encapsulation is about protecting invariants
Over-using immutability when mutation is more efficient:
- Mistake: Persistent data structures everywhere, even in performance-critical code
- Fix: Profile first. Mutable collections are often clearer and faster when properly encapsulated
Making everything pure when side effects are acceptable:
- Mistake: Forcing purity in languages/contexts where it fights idioms
- Fix: Accept that OOP embraces controlled side effects. Encapsulation makes them safe
Not leveraging polymorphism through subtyping:
- Mistake: Using strategy pattern everywhere instead of simple inheritance
- Fix: Inheritance is appropriate for true is-a relationships with substitutability
Avoiding classes when objects would clarify:
- Mistake: Module of functions + data structure when object with methods is clearer
- Fix: Objects make sense when behavior and data are tightly coupled and invariants matter
Thinking in transformations when stateful objects are clearer:
- Mistake: Functional pipelines for inherently stateful workflows
- Fix: State machines, objects with lifecycle. Not everything is a transformation
Not using encapsulation's benefits:
- Mistake: Public fields because "data should be transparent"
- Fix: Private state, public interface. Encapsulation protects invariants and enables change
<related_skills>
Related Skills
- software-engineer — Core engineering philosophy, hexagonal architecture, system design
- functional-programmer — When functional approaches are clearer (transformations, concurrency)
- test-driven-development — Testing philosophy and TDD principles
- java-programmer, python-programmer, swift-programmer — Language-specific OOP idioms </related_skills>
<safety_constraints>
Safety Constraints
<inheritance_safety>
Inheritance Safety
- Never create inheritance hierarchies deeper than 3 levels without exceptional justification
- Never use inheritance solely for code reuse—composition exists for this
- Always verify Liskov Substitution holds before creating a subclass
- Never override methods to do nothing or throw "not implemented" exceptions </inheritance_safety>
<encapsulation_safety>
Encapsulation Safety
- Never expose mutable collections directly—return copies or unmodifiable views
- Never break encapsulation "just for testing"—if you can't test through public API, design is flawed
- Never make fields public "for convenience"—use proper accessors </encapsulation_safety>
<pattern_safety>
Pattern Safety
- Never use Singleton for anything that could reasonably be injected
- Never apply Observer pattern without clear lifecycle management (subscription cleanup)
- Never use inheritance where composition would work—inheritance is not for code reuse </pattern_safety> </safety_constraints>
Summary
Object-oriented programming emphasizes:
- Encapsulation — Bundle data and behavior, hide internals
- Abstraction — Program against interfaces, defer implementation
- Polymorphism — Treat different types uniformly
- Inheritance — Use sparingly, prefer composition
Apply OOP where it improves design: modeling domains with rich behavior, building extensible frameworks, managing complex state. Use other paradigms where they're clearer: functional for transformations, procedural for utilities. Modern software engineering is multi-paradigm—choose the right tool for each problem.
Footnotes
-
Barbara Liskov and Jeannette Wing. 1994. A behavioral notion of subtyping. ACM Transactions on Programming Languages and Systems 16, 6 (Nov. 1994), 1811–1841. https://doi.org/10.1145/197320.197383 </liskov_substitution> ↩
-
Philip Wadler. 1998. The Expression Problem. Email to the Java Genericity mailing list. http://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt ↩
-
Martin Fowler. 2003. AnemicDomainModel. https://martinfowler.com/bliki/AnemicDomainModel.html </anemic_domain_model> ↩
이 스킬의 파일
1object-oriented-programmer skill 질문
Claude Code에 object-oriented-programmer skill을 어떻게 설치하나요?
npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/object-oriented-programmer ~/.claude/skills/object-oriented-programmer를 실행합니다. Claude Code는 ~/.claude/skills(모든 프로젝트) 또는 .claude/skills(한 프로젝트)에서 skills를 로드하며, 설명이 작업과 일치할 때 skill을 사용합니다.
object-oriented-programmer skill을 사용할 수 있는 클라이언트는 무엇인가요?
Agent Skills 형식을 읽는 모든 클라이언트(Claude Code, Codex, Gemini CLI, claude.ai(ZIP으로 업로드))가 사용할 수 있습니다. skill 내 스크립트는 클라이언트가 코드 실행을 허용하는 경우에만 실행됩니다.
특정 언어 전용 SKILL 대신 이 SKILL을 언제 사용해야 하나요?
Java, C#, C++, Python, Ruby, Swift와 같은 객체 지향 언어를 사용 중이지만 특정 언어 전용 SKILL을 사용할 수 없는 경우 이 SKILL을 사용하십시오.
상속과 합성의 차이점은 무엇인가요?
상속은 추가하기는 쉽지만 결합도가 높아 변경이 어렵습니다. 반면 합성은 보일러플레이트 코드가 많아 추가하기는 더 까다롭지만, 영향 범위가 국소적이어서 변경하기가 더 쉽습니다.
SOLID 원칙이란 무엇인가요?
SOLID는 단일 책임(Single Responsibility), 개방-폐쇄(Open-Closed), 리스코프 치환(Liskov Substitution), 인터페이스 분리(Interface Segregation), 의존성 역전(Dependency Inversion) 원칙을 포함하여 객체 지향 설계를 위한 휴리스틱을 제공합니다.