또 같은 코드를 썼습니까?Zenject Signals에서 볼 수 있는 희소한 결합의 위력.

14031 단어 ZenjectUnity

지난번 보도는 여전히'긴밀한 결합'이었다.


저번 보도에서는 Zenject Bindings를 사용하여 모듈 간 객체 참조를 분리하는 방법을 소개했다.
하지만 사실 큰 문제가 남아있어..그것은 "Button 등급을 바꿀 수 없다"는 문제다.
예를 들어'UGUI의 Buton을 누르면 큐브 표시'가 개발 초기 디버깅 목적이었는데, 이번에는 최종 사양인'월드스페이스 내 버튼 대상에 근접해 X 버튼을 누르면 큐브가 표시된다'는 것을 구현했다.
현황 스크립트는 다음과 같습니다.
ButtonObserverDI.cs
using UnityEngine;
using UnityEngine.UI;
using Zenject;

[RequireComponent(typeof(Renderer))]
public class ButtonObserverDI : MonoBehaviour {
    [Inject]
    Button button;

    Renderer targetRenderer;

    void Start() {
        button.onClick.AddListener(OnClick);
        targetRenderer = GetComponent<Renderer>();
    }

    void OnClick() {
        targetRenderer.enabled = true;
    }
}
이 코드는 버튼 클래스가 아니면 이동할 수 없습니다."근처에서 X 단추를 눌렀을 때 Invoke onClick 이벤트"클래스 ProximityButton 가 있다면 다음 스크립트를 다시 준비해야 합니다.
ProximityButtonObserver.cs
using UnityEngine;
using UnityEngine.UI;
using Zenject;

[RequireComponent(typeof(Renderer))]
public class ProximityButtonObserver : MonoBehaviour {
    [Inject]
    ProximityButton button;

    Renderer targetRenderer;

    void Start() {
        button.onClick.AddListener(OnClick);
        targetRenderer = GetComponent<Renderer>();
    }

    void OnClick() {
        targetRenderer.enabled = true;
    }
}

정말 귀찮아요!!


이는 DRY 원칙(Don't Repeat Yourself)을 위반한 것입니다.온클릭 함수의 내용이 바뀌었을 때는 2개 다 변경해야 하기 때문에 조금만 잘못하면 디버깅용 학급과 행동거지가 달라 비참한 사건으로 변할 위험성도 있다.가장 중요한 것은 기존의 Button ObserverDI 클래스의 참고를 모두 Proximity Button Observer 클래스로 바꾸어야 한다는 것이다.Buton ObserverDI 클래스를 덮어쓰더라도 Proximity Button뿐만 아니라 Togle Proximity Button 클래스도 나오면... 끝이 없어요.
왜 이렇게 됐을까? Buton ObserverDI류가 Buton류에 밀접하게 결합되어 있기 때문이다.밀합이란 한 학급이 다른 학급에 강하게 의존해 다른 학급을 바꿀 수 없는 상태를 말한다.
(참고로 우리도 Renderer반에 밀접하게 결합했다.)

드문드문 결합하다


이 상황을 열기 위해서, Buton ObserverDI 클래스를 소키 (밀접된 반의어) 로 삼읍시다.드문드문 결합을 통해 Buton급이든 Proximity Buton급이든 Togle Proximity Buton급이든 이동할 수 있다.
여기에는 두 가지 방법을 소개한다.

C# 인터페이스 사용


부톤반과 프록시미티부톤반의 공통점은'당하면 인북 온 클릭 활동'이다.그리고 Buton ObserverDI반에 필요한 것이 바로 이 점이다.
따라서 우선 이 공통점을 인터페이스로 삼아야 한다.
IClickEventDispatcher.cs
using System;

public interface IClickEventDispatcher {
    event Action onClick;
}
그리고 Buton에게 이 반을 설치했다고 썼다.
ClickEventDispatcher.cs
using System;
using UnityEngine;

public class ClickEventDispatcher : MonoBehaviour, IClickEventDispatcher {
    public event Action onClick;

    public void Fire() {
        onClick.Invoke();
    }
}
검사자에서 Buton의 OnClick 이벤트에 대한 Fire 함수를 설정합니다.
마지막으로 Buton, Proximity Buton과 드문드문 결합된 학급은 다음과 같다.
ClickEventObserver.cs
using UnityEngine;
using Zenject;

[RequireComponent(typeof(Renderer))]
public class ClickEventObserver : MonoBehaviour {
    [Inject]
    IClickEventDispatcher clickEventDispatcher;

    Renderer targetRenderer;

    void Start() {
        clickEventDispatcher.onClick += OnClick;
        targetRenderer = GetComponent<Renderer>();
    }

    void OnClick() {
        targetRenderer.enabled = true;
    }
}
검사기에서 이런 느낌.
Zenject Binding의 Bind Type은 All Interface죠.

이렇게 되면 IClick Evente Dispatcher 인터페이스를 설치한 학급이라면 모든 학급에서 큐브의 디스플레이를 전환할 수 있다.
이번 경우 Proximity Buton은 Click Evente Dispatcher 레벨에 의존할 수도 있고, IClick Evente Dispatcher가 설치된 Proximity Buton 레벨을 제작할 수도 있다.
아이고, 이렇게 하면 시용한 실복을 망가뜨리지 않을 거야. 본격적인 실복이 곧 시작될 거야.

Zenject Signals 사용


이번 경우처럼 Observer 모드를 사용하는 경우가 많아서 그런지 젠ject에는 시그널스 기능이 있다.이것을 사용하면 버튼과 Proximity 버튼류를 희소하게 결합할 수 있다.
먼저 Buton 및 Proximity Buton에서 Fire의 Signal을 정의합니다.
ClickSignal.cs
public class ClickSignal {}
Installer를 작성하여 이 Signal을 Contaainer에 등록하려고 합니다.
ClickSignalInstaller.cs
using Zenject;

public class ClickSignalInstaller : MonoInstaller<ClickSignalInstaller> {
    public override void InstallBindings() {
        SignalBusInstaller.Install(Container);
        Container.DeclareSignal<ClickSignal>();
    }
}
이 Installer를 GameObject에 로그인하고 Scene Context의 Installers 목록에 로그인합니다.
지금까지 사용된 Zenject Bindings는 선택한 Component를 Contaainer에 등록하는 Installer를 자동으로 생성하는 구성 요소입니다.Zenject는 컨테이너에 로그인한 정보를 사용하여 [Inject]가 있는 필드 등에서 Dependency Injection을 하는 느낌이다.
Fire Signal의 한쪽은 이렇게 적혀 있습니다.
FireClickSignal.cs
using UnityEngine;
using Zenject;

public class FireClickSignal : MonoBehaviour {
    [Inject]
    SignalBus signalBus;

    public void Fire() {
        signalBus.Fire<ClickSignal>();
    }
}
Fire 함수를 Buton의 OnClick 이벤트에 등록합니다.Proximity Buton이면 직접SignalBus.Fire 가능합니다.
Observe라는 시그널의 한쪽은 이런 느낌이다.방법에 추가할 수도 있다[Inject].초기화에만 사용되는 변수라면 이 필드를 줄일 수 있을 것 같습니다.Awake/Start 함수와 Injection의 앞뒤도 신경 쓸 필요가 없습니다.
ClickObserver.cs
using UnityEngine;
using Zenject;
using UnityEngine.Events;

public class ClickObserver : MonoBehaviour {
    public UnityEvent OnClick = new UnityEvent();

    [Inject]
    void Init(SignalBus bus) {
        bus.Subscribe<ClickSignal>(() => { OnClick.Invoke(); });
    }
}
ClickObserver.원클릭에서 렌더에 로그인하는 enabled는 끝입니다.
클릭오브 서버 클래스를 보면 버튼과 Proximity Button 클래스에 대해 아무런 참고 없이 희소하게 결합했다.
장면 및 검사자는 다음과 같습니다.

보태다


클릭시그널 클래스에 필드를 추가하면Fire 측은 정보를 추가하여 Observer에서 꺼낼 수 있습니다.이걸 사용하면 큐브뿐만 아니라 Sphere와 Captsule도 가능하다.

총결산


지난번 보도에서 가장 중요한 해산 결합을 언급하지 않았기 때문에 이번에는 희소 결합 디자인의 예를 썼다.
예를 들어 젠ject가 이제 막 시작했기 때문에 댓글과Twitter에서 많이 가르쳐 주셨으면 좋겠어요.
다음 기사에 젠ject의 자동 테스트를 사용했다고 써보고 싶어요.
그럼 안녕!

좋은 웹페이지 즐겨찾기