import ( "코딩", "행복", "즐거움" )

중재자 패턴 ( Mediator Pattern ) 본문

소프트웨어 아키텍처

중재자 패턴 ( Mediator Pattern )

더코드마니아 2023. 3. 27. 12:53

중재자 패턴(Mediator Pattern)은 객체 간의 상호작용을 캡슐화하여 객체 간의 결합도를 낮추는 디자인 패턴입니다.

이 패턴에서 중재자(Mediator)는 객체 간의 통신을 총괄하는 객체로서, 객체 간의 직접적인 상호작용을 허용하지 않고 중재자를 통해 간접적인 상호작용을 수행하도록 합니다. 이를 통해 객체 간의 결합도를 낮출 수 있습니다.

중재자 패턴은 다음과 같은 구성 요소로 구성됩니다.

 

구성요소

중재자(Mediator): 객체 간의 상호작용을 총괄하는 객체입니다.
동료(Colleague): 중재자를 통해 상호작용을 수행하는 객체입니다.

 

중재자패턴의 장점

객체 간의 결합도가 낮아져 객체를 수정하거나 추가하는 일이 쉬워집니다.
객체 간의 상호작용이 중재자를 통해 이루어지므로 코드의 복잡성이 낮아집니다.
객체 간의 상호작용 로그를 쉽게 기록할 수 있습니다.
하지만 중재자 패턴을 사용하면 중재자 객체가 모든 상호작용을 총괄하므로 중재자 객체가 복잡해지는 문제가 있습니다. 또한, 중재자 객체가 너무 많은 역할을 수행하게 되면 단일 책임 원칙(Single Responsibility Principle)을 위배할 수 있습니다

 

주로 사용하는곳??

중재자 패턴은 다음과 같은 상황에서 사용될 수 있습니다.

  1. 객체 간의 상호작용이 복잡한 경우: 객체 간의 상호작용이 복잡하고 많은 경우 중재자 패턴을 사용하여 객체 간의 결합도를 낮출 수 있습니다.
  2. 객체 간의 결합도를 낮추고자 하는 경우: 중재자 패턴을 사용하면 객체 간의 직접적인 상호작용을 허용하지 않고 중재자를 통해 간접적인 상호작용을 수행하므로 객체 간의 결합도를 낮출 수 있습니다.
  3. 객체 간의 상호작용 로그를 기록하고자 하는 경우: 중재자를 통해 상호작용을 수행하므로 중재자 객체에서 객체 간의 상호작용 로그를 쉽게 기록할 수 있습니다.

예를 들어, GUI 애플리케이션에서 버튼, 텍스트 상자, 체크박스 등의 컴포넌트들 간의 상호작용이 많은 경우, 중재자 패턴을 사용하여 컴포넌트 간의 결합도를 낮출 수 있습니다. 또한, 중재자를 통해 상호작용 로그를 기록할 수 있어 디버깅이나 유지보수를 용이하게 할 수 있습니다.

 

// Mediator 인터페이스
type Mediator interface {
    SendMessage(string, string)
}

// User 구조체
type User struct {
    name     string
    mediator Mediator
}

func NewUser(name string, mediator Mediator) *User {
    return &User{
        name:     name,
        mediator: mediator,
    }
}

// Mediator 구조체
type Chatroom struct {
    users map[string]*User
}

func NewChatroom() *Chatroom {
    return &Chatroom{
        users: make(map[string]*User),
    }
}

func (c *Chatroom) AddUser(user *User) {
    c.users[user.name] = user
}

func (c *Chatroom) SendMessage(sender, message string) {
    for name, user := range c.users {
        if name != sender {
            user.ReceiveMessage(sender, message)
        }
    }
}

// User의 SendMessage 메서드
func (u *User) SendMessage(message string) {
    u.mediator.SendMessage(u.name, message)
}

// User의 ReceiveMessage 메서드
func (u *User) ReceiveMessage(sender, message string) {
    fmt.Printf("%s received message from %s: %s\n", u.name, sender, message)
}

func main() {
    chatroom := NewChatroom()

    john := NewUser("John", chatroom)
    jane := NewUser("Jane", chatroom)

    chatroom.AddUser(john)
    chatroom.AddUser(jane)

    john.SendMessage("Hi, Jane!")
    jane.SendMessage("Hello, John!")
}

위 코드에서는 Chatroom 구조체가 중재자 역할을 수행합니다. Chatroom 구조체는 Mediator 인터페이스를 구현하며, SendMessage 메서드를 통해 메시지를 전달합니다. User 구조체는 이름과 중재자(Mediator)를 가지고 있으며, SendMessage 메서드를 호출하여 메시지를 보낼 수 있습니다.

즉, User 구조체는 SendMessage 메서드를 통해 직접 다른 User와 통신하지 않고, 중재자(Chatroom)를 통해 간접적으로 메시지를 전달합니다. Chatroom 구조체는 중재자로서 모든 User들의 메시지를 총괄 관리하고, 각 User들의 메시지를 전달하는 역할을 합니다. 이렇게 하면 User 구조체 간의 직접적인 결합도가 낮아지고, 유연성과 확장성이 높아집니다.

따라서, 위 코드에서 Chatroom 구조체가 중재자 역할을 수행하여 중재자 패턴이 사용되었습니다.

 

package main

import "fmt"

// Mediator 인터페이스
type Mediator interface {
	RegisterPlayer(player *Player)
	NextTurn(player *Player)
}

// Player 구조체
type Player struct {
	name     string
	position int
	mediator Mediator
}

func NewPlayer(name string, mediator Mediator) *Player {
	return &Player{
		name:     name,
		position: 1,
		mediator: mediator,
	}
}

// Mediator 구조체
type Game struct {
	players       []*Player
	currentPlayer int
}

func NewGame() *Game {
	return &Game{
		players:       []*Player{},
		currentPlayer: 0,
	}
}

func (g *Game) RegisterPlayer(player *Player) {
	g.players = append(g.players, player)
}

func (g *Game) NextTurn(player *Player) {
	if player == g.players[g.currentPlayer] {
		g.currentPlayer = (g.currentPlayer + 1) % len(g.players)
		fmt.Printf("%s's turn ended. Next turn: %s\n", player.name, g.players[g.currentPlayer].name)
	}
}

// Player의 EndTurn 메서드
func (p *Player) EndTurn() {
	p.mediator.NextTurn(p)
}

func main() {
	game := NewGame()

	player1 := NewPlayer("Player 1", game)
	player2 := NewPlayer("Player 2", game)
	player3 := NewPlayer("Player 3", game)

	game.RegisterPlayer(player1)
	game.RegisterPlayer(player2)
	game.RegisterPlayer(player3)

	fmt.Printf("First turn: %s\n", game.players[0].name)

	for i := 0; i < 3; i++ {
		player1.EndTurn()
		player2.EndTurn()
		player3.EndTurn()
	}
}

위 코드에서는 Game 구조체가 중재자 역할을 수행합니다. Game 구조체는 Mediator 인터페이스를 구현하며, RegisterPlayer 메서드를 사용하여 플레이어를 등록하고, NextTurn 메서드를 구현하여 현재 플레이어의 턴이 종료되면 다음 플레이어의 턴으로 넘어가도록 합니다.

Player 구조체는 EndTurn 메서드를 호출하여 메시지를 보내면서 자신의 턴을 종료합니다. 이때, 중재자(Game)를 통해 다음 플레이어의 턴으로 넘어가도록 구현되어 있습니다.

따라서, 위 코드에서 Game 구조체가 중재자 역할을 수행하여 중재자 패턴이 사용되었습니다.

 

위 코드에서 중재자 패턴을 사용함으로 인해 다음과 같은 이점이 있습니다.

  1. 플레이어 객체의 결합도 감소: 중재자 패턴을 사용하면 플레이어 객체들은 직접적으로 서로 통신하지 않고 중재자를 통해 간접적으로 통신하므로, 객체 간의 결합도가 낮아집니다. 이로 인해 유연성과 확장성이 높아지고, 객체 간의 의존성이 줄어듭니다.
  2. 턴 순서 관리의 용이성: 중재자 패턴을 사용하여 플레이어들의 턴 순서를 관리하면, 턴 순서가 변경되는 경우 중재자만 수정하면 됩니다. 이로 인해 유지보수성과 확장성이 높아집니다.
  3. 코드의 가독성과 이해의 용이성: 중재자 패턴을 사용하면 객체 간의 상호작용이 중재자 객체를 통해 이루어지므로 코드의 가독성과 이해의 용이성이 높아집니다.

따라서, 위 코드에서 중재자 패턴을 사용하여 플레이어 객체의 결합도를 감소시키고, 턴 순서를 관리하는 등의 이점을 얻을 수 있습니다.