Unity essence ☀️ Brother, you know so many "design patterns". Will the interviewer love you

Hello, everyone, your brother orange suddenly appears~

Blog address of this series: Portal

I talked to you about the universal lock solution during the interview a few days ago,

The question in the interviewer's gene at that moment, and the "Unity design pattern"

 

Brother orange will show you the common design patterns of Unity today

1, Singleton mode

Singleton pattern is a common pattern in design patterns. Its purpose is to expect a class to have only one instance,

And provide a global access point to access it.

 

Multiple identical singleton scripts cannot exist in a scenario at the same time, because the function of singleton script is through method: class instance.xxx to access the script,

If there are multiple identical scripts, the method will not know which singleton to call.

 

There are two ways to write singleton mode. One is to write singleton code for each script

The other is to write a singleton code script, and other scripts that want to implement singleton mode can inherit it.

 

First, let's look at the first one:

1. Singleton class of Unity version

Initial state of Test script:

using UnityEngine;

public class Test : MonoBehaviour
{
}

Test script singleton mode:

using UnityEngine;

public class Test : MonoBehaviour
{
    public static Test Instance;

    private void Awake()
    {
        Instance = this;
    }
}

 

Single use method:

Mount the Test script on the object you want to control,

Now you can call the script in any script after the Awake life cycle.

print(Test.Instance.gameObject.name);

 

2. Generic singleton template

The above method needs to write code in each script. A little makes a mickle, and it's a little troublesome

After all, it is a high (lazy) efficiency (lazy) talent who can save. How can it be written like this

This method only needs a single instance template class,

Subsequent scripts inherit this class, and subsequent scripts implement the singleton mode.

There is a flaw in FindObjectOfType on the Internet,

When the scenario is closed, other methods cannot call the singleton. The following singleton template solves this problem.

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

public class BaseWindow<T> : MonoBehaviour where T : MonoBehaviour
{
    static T instance;

    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                // Look in the scene first
                List<T> ts = Skode_GetTObjs<T>();

                // If it cannot be found in the scene, an error will be reported
                if (ts.Count == 0)
                {
                    Debug.Log("The script cannot be found for this scenario:" + typeof(T));
                    return instance;
                }

                instance = ts[0];

                if (ts.Count > 1)
                {
                    foreach (var VARIABLE in ts)
                    {
                        Debug.Log("There are multiple scenes" + VARIABLE, VARIABLE.gameObject);
                    }
                }
            }

            return instance;
        }
    }

    /// <summary>
    ///Get all objects with T components in the scene
    /// </summary>
    public static List<T> Skode_GetTObjs<T>() where T : MonoBehaviour
    {
        List<T> objectsInScene = new List<T>();
        //This method will be found together with the preform. So GameObject scene. Name can filter out preforms
        foreach (T go in Resources.FindObjectsOfTypeAll<T>().ToList())
        {
            if (!(go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave ||
                  go.gameObject.scene.name == null))
                objectsInScene.Add(go);
        }

        return objectsInScene;
    }
}

 

usage method:

1. The BaseWindow method can be placed in Assets.

2. To implement the script inheritance BaseWindow of the singleton,

public class Test : BaseWindow<Test>
{
}

3. Other methods can call the singleton script

print(Test.Instance.gameObject.name);

 

 

2, Observer mode

Defines one to many dependencies between objects,

In this way, when an object (the observed) changes state, all its dependencies (the observer) will be notified.

 

Our script:

The base class in the figure below is easy to understand, which is convenient for us to reuse and expand other groups of observers and observed;

Here, two observers and one observed are realized;

The program is started in the Start of ObserverMode.

(the last script is "an observer")

 

Now the brothers may have questions:

Does the observer get information in update, which will consume a lot of resources?

unable.

Later, we will find that when the state of the observed changes, the observer only executes the code once.

The observer does not take the initiative to obtain information.

The state change of the observed is written in attributes, and the state change is executed only once.

 

How to use the following script:

The ObserverMode can be placed on the scene object, and other scripts can be placed in Assets.

 

1. Startup class: ObserverMode

using UnityEngine;

public class ObserverMode : MonoBehaviour
{
    /// <summary>
    ///Observed
    /// </summary>
    private Subject subject;

    private void Awake()
    {
        //Instantiate the observer
        subject = new Subject();

        //Instantiate observers A and B
        IObserver observerA = new ObserverA(subject);
        IObserver observerB = new ObserverB(subject);

        //Add observers A and B to the dependency of observer state change
        subject.AddObserber(observerA);
        subject.AddObserber(observerB);
    }

    private void Start()
    {
        //Change the state of the observed and see how the observers A and B react
        subject.State = "state A";
    }
}

 

2. Observed by: Subject

/// <summary>
///Observed
/// </summary>
public class Subject : ISubject
{
    private string mState;

    public string State
    {
        get { return mState; }
        set
        {
            mState = value;
            NotifyObserver();
        }
    }
}

 

3. Observer base class: ISubject

using System.Collections.Generic;
using UnityEngine;

/// <summary>
///Observer abstract class
/// </summary>
public abstract class ISubject
{
    /// <summary>
    ///A collection of all observers who observe this observed person
    /// </summary>
    protected List<IObserver> mObserverList;

    public ISubject()
    {
        mObserverList = new List<IObserver>();
    }

    /// <summary>
    ///Add observer
    /// </summary>
    public void AddObserber(IObserver observer)
    {
        if (mObserverList.Contains(observer) == false && observer != null)
        {
            mObserverList.Add(observer);
            return;
        }

        Debug.Log("report errors");
    }

    /// <summary>
    ///Remove observer
    /// </summary>
    public void RemoveObserber(IObserver observer)
    {
        if (mObserverList.Contains(observer))
        {
            mObserverList.Remove(observer);
            return;
        }

        Debug.Log("report errors");
    }

    /// <summary>
    ///Notify all observers of updates
    /// </summary>
    public void NotifyObserver()
    {
        foreach (IObserver item in mObserverList)
        {
            item.RefreshData();
        }
    }
}

 

4. Observer A: observer A

using UnityEngine;

/// <summary>
///Observer A
/// </summary>
public class ObserverA : IObserver
{
    private Subject subject;
    
    /// <summary>
    ///Constructor, who should be observed when assigning values during initialization
    /// </summary>
    public ObserverA(Subject value)
    {
        subject = value;
    }

    public override void RefreshData()
    {
        Debug.Log("Here is A Observer, observed subjectA The status is:" + subject.State);
    }
}

 

5. Observer B: observer B

using UnityEngine;

/// <summary>
///Observer B
/// </summary>
public class ObserverB : IObserver
{
    private Subject subject;

    /// <summary>
    ///Constructor, who should be observed when assigning values during initialization
    /// </summary>
    public ObserverB(Subject value)
    {
        subject = value;
    }

    public override void RefreshData()
    {
        Debug.Log("Here is B Observer, observed subjectA The status is:" + subject.State);
    }
}

 

6. Observer base class: IObserver

/// <summary>
///Observer base class
/// </summary>
public abstract class IObserver
{
    /// <summary>
    ///Called by the property of the observer. Tell the observer that the data they observe has changed.
    ///Since the observer has assigned the object to be observed when initializing, the observer can use the latest data of the observed object after being informed
    /// </summary>
    public abstract void RefreshData();
}

 

Isn't it very efficient and orderly?

 

3, Agent mode

1. Let's first look at the definition of agent mode:

For some reason, an object needs to be provided with a proxy to control access to the object.

At this time, the access object is not suitable or cannot directly reference the target object. The proxy object acts as the intermediary between the access object and the target object.

Are you confused?

It's actually Delegate

 

The proxy mode is similar to the observer mode. Both define one to many dependencies between objects. When an object changes state, all its dependencies will be notified.

 

2. The difference between agent mode and observer mode is:

Observer mode observes the final object,

The agent model observes the intermediary.

 

In some cases, a customer cannot or does not want to directly access another object. At this time, it needs to find an intermediary to help complete a task. This intermediary is the proxy object.

For example, you don't have to go to the railway station to buy a train ticket. You can buy it through 12306 website or go to the train ticket agency. Another example is to find a girlfriend, a nanny and a job, which can be completed by looking for an intermediary.

 

3. When to use agent mode instead of observer mode in software design?

For example, for security reasons, you can't let others directly access a data, such as transform Position, because others may do something bad, visit transform gameobject. Where's XXX

Then we also need to inform others that the data has been changed. What should we do?

Then we need to use the agent mode.

4. Proxy mode example

I've written a blog before the agency model, which is relatively complete,

Proxy mode delegate link: Portal

 

 

Well, that's all for today's sharing,

Little brother, ah, does it mean that one button three times in a row?

 

In the next section, we will continue to share two design patterns, which are suitable for the interview.

 

If you have technical problems or problems

You can add my vx (skode250)

Talk to me about your story 🧡

Added by kevinn on Wed, 09 Feb 2022 08:42:02 +0200