참치김밥은 최고의 한식이다

[디자인 패턴] 방문자 패턴 본문

[디자인 패턴] 방문자 패턴

l__j__h 2023. 11. 22. 15:23

참고 : https://www.yes24.com/Product/Goods/114854688

참고 : https://unialgames.tistory.com/m/entry/Unity-Tip-Visitor-Pattern

 

방문자 패턴은 클래스 자체를 수정하지 않고도, 새로운 동작을 추가할 수 있는 디자인 패턴입니다.

방문자가 되고자 하는, 또는 방문하려는 모든 클래스는 인터페이스를 구현해야 합니다.

이론만 보면 졸리고 이해가 어려우니..

 

아래 3가지를 기억한 후 바로 코드를 봅시당.

1. '클래스 자체를 수정하지 않고도 새로운 동작을 할 수 있다'
2. 방문자가 되려는 인터페이스(예시. IVisitor)에서는 Visit(여러 IVisitorElement 객체) 함수를 정의해둡니다.

3. 방문하려는 인터페이스(예시. IVisitorElement)에서는 Accept(IVisitor visitor) 함수를 정의해둡니다.

 

코드

방문하려는 클래스(IVisitorElement)

using System;
using System.Collections.Generic;
using UnityEngine;

namespace Chapter.Visitor
{
    public class Weapon : MonoBehaviour, IVisitorElement
    {
        [Header("사정 거리")]
        public int range = 5; // 무기의 사정 거리
        public int maxRange = 25; // 무기의 최대 사정 거리

        [Header("공격력")]
        public float strength = 25.0f; // 무기의 공격력
        public float maxStrength = 50.0f; // 무기의 최대 공격력

        public void Fire()
        {
            Debug.Log("무기 발사!"); // 무기 발사 동작을 나타내는 메시지 출력
        }

        public void Accept(IVisitor visitor)
        {
            visitor.Visit(this); // 방문자(Visitor)에게 현재 무기(Weapon)를 방문하도록 요청하는 메서드
        }

        private void OnGUI()
        {
            GUI.color = Color.green;
            GUI.Label(new Rect(125, 40, 200, 20), "무기 사정 거리: " + range); // 화면에 무기의 사정 거리 출력
            GUI.Label(new Rect(125, 60, 200, 20), "무기 공격력: " + strength); // 화면에 무기의 공격력 출력
        }
    }
}

 

 

using System;
using UnityEngine;

public class Shield : MonoBehaviour, IVisitorElement
{
    public float health = 50.0f; // 실드의 체력

    public float Damage(float damage)
    {
        health -= damage; // 실드 체력에서 입힌 데미지를 감소시킵니다.
        return health; // 남은 체력을 반환합니다.
    }

    public void Accept(IVisitor visitor)
    {
        visitor.Visit(this); // 방문자(Visitor)에게 현재 실드(Shield)를 방문하도록 요청하는 메서드
    }

    private void OnGUI()
    {
        GUI.color = Color.green;

        GUI.Label(new Rect(125, 20, 200, 20), "실드 체력: " + health); // 화면에 실드의 체력을 출력합니다.
    }
}

 

 

방문자 클래스(IVisitor)

public interface IVisitor
{
    void Visit(Weapon weapon);
    void Visit(Shield shield);
}

이제, IVisitor 인터페이스를 상속한 클래스에서는, 두 개의 Visit 함수를 통해 각각 Weapon과 Shield에 어떤 동작을 할지 작성해주면 됩니다.

즉, Weapon과 Shield 클래스를 어떻게 동작시킬지를, Weapon + Shield 클래스에서 수정하는 게 아니라, IVisitor를 상속하는 클래스에서 수정해주면 되는 겁니다.

 

아래처럼요~

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

// CreateAssetMenu 속성을 사용하여 에디터에서 생성 가능한 파워업 스크립터블 오브젝트를 정의합니다.
[CreateAssetMenu(fileName = "PowerUP", menuName = "PowerUP")]
public class PowerUp : ScriptableObject, IVisitor
{
    // 파워업의 속성들을 정의합니다.
    public string powerupName;
    public GameObject powerupPrefab;
    public string powerupDescription;
    [Tooltip("실드를 완전히 회복합니다.")]
    public bool healShield;

    [Range(0.0f, 25)]
    [Tooltip("무기 사거리를 최대 25만큼 늘립니다.")]
    public int weaponRange;
    [Range(0.0f, 50f)]
    [Tooltip("무기 강도를 최대 50%만큼 높입니다.")]
    public float weaponStrength;

    // 방문자 패턴에 따라 무기 객체를 방문하는 Visit 메서드입니다.
    public void Visit(Weapon weapon)
    {
        // 무기의 사거리를 늘립니다.
        int range = weapon.range += weaponRange;
        if (range >= weapon.maxRange)
            weapon.range = weapon.maxRange;
        else
            weapon.range = range;

        // 무기의 강도를 늘립니다.
        float strength = weapon.strength +=
            Mathf.Round(weapon.strength * weaponStrength / 100);
        if (strength <= weapon.maxStrength)
            weapon.strength = weapon.maxStrength;
        else
            weapon.strength = strength;
    }

    // 방문자 패턴에 따라 실드 객체를 방문하는 Visit 메서드입니다.
    public void Visit(Shield shield)
    {
        // 실드를 회복합니다.
        if (healShield)
            shield.health = 100.0f;
    }
}

 

 

위의 방문자와 방문하려는 클래스들을, GameController 와 ClientController 스크립트를 만들어 관리하면 편합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

public class GameController : MonoBehaviour, IVisitorElement
{
    private List<IVisitorElement> _visitorElements = new List<IVisitorElement>();

    private void Start()
    {
        // 게임 오브젝트에 방문 가능한 요소들을 추가합니다.
        _visitorElements.Add(gameObject.AddComponent<Shield>());
        _visitorElements.Add(gameObject.AddComponent<Weapon>());
    }

    public void Accept(IVisitor visitor)
    {
        // 모든 방문 가능한 요소들에 대해 방문자(Visitor)를 수용합니다.
        foreach(IVisitorElement element in _visitorElements)
        {
            element.Accept(visitor);
        }
    }
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ClientVisitor : MonoBehaviour
{
    public PowerUp shieldPowerUp;
    public PowerUp weaponPowerUp;

    private GameController _gameController;

    void Start()
    {
        _gameController = gameObject.AddComponent<GameController>();
    }

    private void OnGUI()
    {
        // 버튼을 통해 각 PowerUp을 게임 컨트롤러에 전달합니다.
        if (GUILayout.Button("실드 파워 업"))
            _gameController.Accept(shieldPowerUp);
        if (GUILayout.Button("무기 파워 업"))
            _gameController.Accept(weaponPowerUp);
    }
}

 

728x90

'' 카테고리의 다른 글

[디자인 패턴] 데커레이터로 무기 시스템 구현하기  (0) 2024.02.21