6. SOLID - DIP
Depend upon abstractions, not concretions
1. 참고 링크
2. 의존성(Dependency) 개념
1. "의존" 이란?
- 依 의지할 의, 存 있을 존
- 다른 것에 의지(依支)하여 존재(存在)함.
2. 상속 관계에서, "화살표 방향"과 "의존관계" 살펴보기


- 상속받은 자식 객체는, 부모 객체의 속성 없이 존재할 수 없다.
- 하위 객체인 Programmer, Dancer, Dog, Iguana 등의 객체는, 상위 객체에 의존한다.
- 화살표의 방향은 의존하고 있는 방향이다.
3. 객체의 의존 관계와, 의존성 주입(DI)를 활용한 의존 역전 구조

3. 의존성 관계 예시
1. 의존성 관계 예시 구조

class Cat:
def speak(self):
print("Meow")
class Dog:
def speak(self):
print("bark")
class Zoo:
def __init__(self):
self.cat = Cat()
self.dog = Dog()
zoo = Zoo()
zoo.cat.speak()
zoo.dog.speak()
- Zoo 객체는, Cat, Dog 객체에 의존된 관계다.
- Zoo 객체는, Cat, Dog 객체 없이 존재할 수 없으며,
- Zoo 객체는, Cat, Dog 객체가 수정될 때, 같이 수정할 필요가 있다.
2. 의존성 관계 예시 코드

class Cat:
def speak(self):
print("Meow")
class Dog:
def speak(self):
print("bark")
# 추가된다고 가정합니다.
class Sheep:
def speak(self):
print("Baaa")
class Cow:
def speak(self):
print("Moo")
# 가장 먼저,
# 의존된 관계에서는 Zoo 객체가 직접 수정되어야 한다.
class Zoo:
def __init__(self):
self.cat = Cat()
self.dog = Dog()
self.sheep = Sheep()
self.cow = Cow()
zoo = Zoo()
zoo.cat.speak()
zoo.dog.speak()
zoo.sheep.speak()
zoo.cow.speak()
- 이후, 객체가 의존된 관계에서 어떤 문제가 발생하는지 확인해야 한다.
4. 문제 파악
1. 가정
위 종속된 객체 구조 예시에서
Sheep, Cow 를 개발자 2명이 각각 구현했다고 가정하고,
이를 업데이트 시키고, 어떤 문제가 발생하는지 확인하기
2. 개발자 A, " Sheep 객체" 구현 후, 기존 코드 적용 작업

3. 개발자 B, "Cow 객체" 구현 후, 기존 코드 적용 작업

4. 개발자 A, 개발자 B 작업 결과 서로 다른 코드로 구현되어 통합 작업 필요

5. 옆에 있는 사람들 3명만 같이, 직접 구현후 통합해보시면, 실감나게 느끼실 수 있습니다.
- 전사, 마법사, 궁수 클래스를 각각 구현하고, 통합 테스트를 진행해보세요
5. 문제 해결(DI, Dependency Injection)
1. 추상화
Q. 클래스 간의 의존 관계를 어떻게 제거할 수 있을까?
A. 클래스가 추상화된 인터페이스를 구현하여 대신 의존하게 합니다.
>> 의존성 주입(Dependency Injection)을 통한 제어 역전(Inversion of Control) 상태 구현
2. 용어
추상화
인터페이스
의존성
의존성 주입(Dependency Injection)
제어 역전(Inversion of Control)
3. 추상화
추상 사전적 정의
추상(抽 뽑을 추 象 코끼리 상)
>> 여러 가지 사물(事物)이나 개념(槪念)에서 공통(共通)되는 특성(特性)이나 속성(屬性) 따를 추출(抽出)하여 파악(把握)하는 작용(作用).

4. 의존성 주입(Dependency Injection)


# 추상화된 인터페이스를 생성한다.
class Animal:
def speak(self):
pass
class Cat:
def speak(self):
print("Meow")
class Dog:
def speak(self):
print("bark")
# 추가된다고 가정합니다.
class Sheep:
def speak(self):
print("Baaa")
# Zoo 객체는 수정되지 않는다.
class Zoo:
def __init__(self):
self.animals = []
def addAnimals(self, animal):
self.animals.append(animal)
def run(self):
for animal in self.animals:
animal.speak()
zoo = Zoo()
zoo.addAnimals(Cat())
zoo.addAnimals(Dog())
zoo.addAnimals(Sheep())
zoo.run()
6. C# DI 예제와 MVVM
1. 의존성 문제가 있는 소스코드
using System;
using System.Collections.Generic;
// 추상화된 인터페이스 없이 구체 클래스 직접 사용
class Cat
{
public void Speak()
{
Console.WriteLine("Meow");
}
}
class Dog
{
public void Speak()
{
Console.WriteLine("Bark");
}
}
class Sheep
{
public void Speak()
{
Console.WriteLine("Baaa");
}
}
// Zoo 클래스가 구체 클래스에 직접 의존함
class Zoo
{
private List<object> animals = new List<object>();
public void AddCat()
{
animals.Add(new Cat());
}
public void AddDog()
{
animals.Add(new Dog());
}
public void AddSheep()
{
animals.Add(new Sheep());
}
public void Run()
{
foreach (var animal in animals)
{
if (animal is Cat cat)
cat.Speak();
else if (animal is Dog dog)
dog.Speak();
else if (animal is Sheep sheep)
sheep.Speak();
}
}
}
class Program
{
static void Main()
{
Zoo zoo = new Zoo();
zoo.AddCat();
zoo.AddDog();
zoo.AddSheep();
zoo.Run();
}
}
2. 아래 C# 소스코드를 Model, View, ViewModel로 사용한다면, 어떻게 만들 수 있을까요?
C# 콘솔 프로젝트에서 Model, View, ViewModel로 분할해보며 MVVM 감을 잡아봅시다.
using System;
using System.Collections.Generic;
// 추상화된 인터페이스 생성
interface IAnimal
{
void Speak();
}
// 다양한 동물 클래스 구현
class Cat : IAnimal
{
public void Speak()
{
Console.WriteLine("Meow");
}
}
class Dog : IAnimal
{
public void Speak()
{
Console.WriteLine("Bark");
}
}
class Sheep : IAnimal
{
public void Speak()
{
Console.WriteLine("Baaa");
}
}
// Zoo 클래스는 수정되지 않음
class Zoo
{
private List<IAnimal> animals = new List<IAnimal>();
public void AddAnimal(IAnimal animal)
{
animals.Add(animal);
}
public void Run()
{
foreach (var animal in animals)
{
animal.Speak();
}
}
}
class Program
{
static void Main()
{
Zoo zoo = new Zoo();
zoo.AddAnimal(new Cat());
zoo.AddAnimal(new Dog());
zoo.AddAnimal(new Sheep());
zoo.Run();
}
}
3.
7. 의존성과 MVVM
1.
MVVM 패턴은 별다른 기술이 아닙니다.
프로젝트 관점에서, 소스코드를 역할에 맞게 분할해 관리하는 분류 체계일 뿐입니다.
그리고 분할된 C#파일들의 Data와 Logic을 SOLID 원칙을 지키며 이어주는 DataBinding, Command 와 같은 기술을 사용할 뿐입니다.
2.
C언어를 처음 배울 때, Main() 함수에 모든 소스코드를 밀어넣어 작성하고, 이후 함수를 만들고, 소스 파일을 쪼개고, 라이브러리로 구현합니다. 하지만, 굳이 하나의 파일에서 작성하겠다면, 불가능한 것은 아닙니다.
C# WPF 프로젝트도 View 하나와 CodeBehind 하나에서 출발합니다.
그리고, 하고자 한다면 CodeBehind 하나에 모든 Data와 Logic을 구현하는 것이 불가능하지는 않습니다.
문제는 그 방법이, 비효율적이고, 수많은 부작용이 발생합니다.
3.
Model은,
Model 역할을 하는 클래스들의 집합으로
View는,
View 역할을 하는 클래스들의 집합으로
ViewModel은,
ViewModel 역할을 하는 클래스들의 집합으로
그리고, 이렇게 분할된 소스코드들의 Data와 Logic이 상호 연동되어 실행하기 위한
DataBinding, Command 와 같은 기술을 추가 사용할 뿐입니다.
4.
C#과 WPF XAML을 사용하는 프로젝트를 통해 어느정도 익숙해지셨다면
1) MVVM 학습의 첫 단계는, 객체간 의존성 문제의 이해입니다.
2) WPF CodeBehind에서 의존된 객체들을 MVVM 역할에 맞게 분할하고
3) DataBinding, ICommand 등을 사용해, 분할된 소스코드 파일을 하나의 파일처럼 사용하는 방법을 익힙니다.
8. DI
1. 생성자 주입 방식
2. 메서드 주입 방식
3. 인터페이스를 통한 주입 방식
4. 서비스 로케이터 방식