Using events within interfaces to achieve loose coupling.

I am trying to become better at programming loosley coupled code, though I have stumbled upon a design which I am not sure is good practice, and I would like to know from you guys whether this approach is good practice.

My idea is to make an interface for NPC’s who can be possible targets. Targetable NPC’s would have to implement this ITarget interface:

public delegate void VulnerabilityHandler();

public interface ITarget {

    event VulnerabilityHandler vulnerabilityChanged;
}

The idea with this interface is that all targetable NPC’s must implement a vulnerability event, which other classes can subscribe to and become informed if vulnerability is changed on that particular NPC.

However, when implementing this interface into a class, I am required to write a lot of extraordinary code.

public class Player : MonoBehaviour, ITarget {

    event VulnerabilityHandler vulnerabilityChangeEvent;
    event VulnerabilityHandler ITarget.vulnerabilityChanged
    {
        add
        {
            vulnerabilityChangeEvent += value;
        }
        remove
        {
            vulnerabilityChangeEvent -= value;
        }
    }
}

For every event, I wish to implement I am required to implement the above code, which I find rather extensive. I personally believe this is a good approach, because it allows for NPC’s to have loose coupled targets, though I am pondering if there exist better solutions.

It’s not that much code, is it? It’s a standard declaration and a standard property with a minimal accessor/mutator.

Interfaces in general, especially event interfaces (as long as such interfaces are shared accros multiple classes) are always a good idea and good practice. Dont worry about the extra code, it’s definitly an OOP and good layout.

Furthermore, the other choice would be using an abstract class but this does introduce limitations in your inheritance hierarchy.

Perhaps it is not that much, when you put it like that. I often just use code which requires fewer lines, mostly because I rarely use getters / setters. I guess thats another aspect I might also have to look into.

I guess the limitation in the abstract classes is because you only can inherit from a single class?

Exactly. Other than that, deriving multiple different classes from a dedicated base class is considered bad design. :wink:

What do you mean by this? That’s the entire purpose of having a “base” class, to be derived from it. I agree that overuse/deep/complex hierarchies are typically bad, but I wouldn’t dream of drawing the line at “multiple”, which I interpret as “more than one”.

Anyway, for the OP, check out the concept of “composition”. You can do super flexible stuff with that.

I already thought that my previous post would be missunderstood. What I mean is, that it fully depends on what that certain baseclass represents. In terms of MonoBehaviours its completly legit since they represent the base of every behaviour, attachable to a GameObject.
What I mean is that something so dedicated and single-usefull like vulnerability shouldnt be represented by a base class. Something this dedicated should always be moved to an interface. In contrast, a base class of type “ControlledEntity” (or sth similar) which would represent monsters/NPC’s and what-so-ever would be legit. Letting such a base class implement a vulnerability interface would be considered good design.

/E: What I meant with “multiple different classes” are classes which derive from a certain dedicated base class, tho, they dont share any similarity, neither with the base class nor with each other, falling out of context.

System like this is very fast running, and the only problem with it is modifications. Changing interface will make broken all classes that implement this interface. You might want to create another interface version 2.0 and you can end up with lots of interfaces or lots of classes to change when you change your single interface. If you decide to add some “features” in the future it can become time consuming.

If it’s an issue for you, then you can use an event system based on broadcasts, event managers and things like that.
If not then it’s a great system.

But that is exactly what a MonoBeahviour is, a base class for “multiple different classes” that don’t share any similarity. And you just cited MonoBehaviour as an example of good design.

Nice one, but obviously, anything deriving from MonoBehaviour shares a big similarity, it is a behaviour, attachable to any GameObject! ;D
Furthermore, MonoBehaviours are not dedicated and force you into one single meant functionality.

Yeah, I think I get your point. If I have implemented the ITarget interface in five different classes, if I wish to further add features in the ITarget interface, I am also required to modify the five other classes.

I am however not quiet sure of your solutions. Isn’t broadcast often frowned upon, because of using magic strings (Executing functions based on strings rather than using references)? How would you design an event manager to overcome this problem, I can’t seem to grasp my head around a solution for such issue.

I assure you that I’m not trying to impress you. But thank you anyways.

And any set of classes that inherit from any single base class—even an empty one—share the big similarity that they also implement a behavior of some sort. That is the point of classes in OO, you see, which is to implement behavior. This similarity you’re imagining in all MonoBehaviour-derived classes is so broad as to be useless. It’s like you likening yourself to a sea urchin. Both of you are members of the animal kingdom, but that says very little else about either of you.

So MonoBehaviour is both specialized enough that classes derived from it are similar enough to makes them okay in your OOP design book, and, at the very same time, its general purpose enough that it doesn’t force derived classes to be similar. So, according to your definition, it’s both good (specialized) and bad (too broad).

It can’t be both at the same time. Care to make a pick?

Pub-sub messaging systems don’t need to rely on magic strings. There’s a nice open source library called “TinyMessenger” that does pub-sum messaging quite well, and it’s all in a single C# file. I have been using it on several projects with great success. I wrote about my experience with it a while back.

1 Like

I guess you should re-read my previous posts.
To explain it a bit better:

  1. Deriving from an empty base class does not implement a behaviour in any way.
  2. Deriving from MonoBehaviour does implement the fact of being a behaviour. It doesnt matter what the derived classes acctually do and how different they are, they always stay the big global type, called behaviour.
  3. The similarity they share/implement are in no way useless. Its global, no matter what you derive from it, its always obviously attachable to a game object.
  4. Regarding to specialized nor broad. MonoBehaviour is nothing of both. Anything deriving from MonoBehaviour does obviously cleary mirror the fact of being a behaviour. It is clearly not specialized since you always maintain the main and global similarity, but infact can build fairly different classes from it. Still, it clearly stays a behaviour.

I guess its hard to explain what im up to say, tho, I clearly get your point. Its true that any class implements some type of behaviour when it derives from another. In terms of being vulnerable, its something so specialized, in general, a so specialized property of an object to better be an interface.

in what way is this decoupled?

1 Like

I see what Sharp Development is getting at and I think it might just come down to this use case. Making a base class wouldn’t allow an NPC to be vulnerable and also be something else.

I would have, but then you said:

Not exactly a glowing endorsement by the post’s own author.

You use the word “behavior” as if it means something specific, and yet you directly follow that with explaining that derived classes are free to do whatever they want to do. What’s the use of “being a behavior” in OO design terms if it doesn’t carry any constraints or impose any, for lack of a better word, actual behavior?

And why should I care or even want a type to be attached to GameObject if it’s not going to interact with rendering, physics, or user input directly? Why should I want a type to inherit from MonoBehaviour if all it is doing is communicating with a server or logging diagnostics? Yet that’s what we are often forced to do just to be able to function in Unity’s runtime.

You keep going around the same merry-go-around. You say that deriving from MonoBehaviour is important because the derived type is now a behavior as if that somehow matters and then go back to saying that that still allows the derived class to do whatever it wants as if being a behavior no longer matters anymore. Are you practicing some sort of code mysticism here where any class is always is and isn’t, always broad and yet specialized, always “is nothing of both”? Because that’s groovy, man. But you’ll need to share your stash because we’re not at all on the same wavelength.

I think that whatever point you’re trying to make would go down a lot smoother and have a chance of actually making sense if you would stop using MonoBehaviour as an example of good OOP design, because it simply isn’t. You’ll not see such a monolithic class design in your Gang of Four or your Martin Fowler or in your Domain Driven Design books. It’s a compromise born out of the constraints of a language and the desire to present users with an API that is easy to understand and doesn’t feature dozens of interfaces that need to be composed together to make things work. And that’s just fine. OO is a means to an end, not an end by itself.

That’s a very good point, actually.

1 Like

I have an additional question in regards to interfaces. I have now created an additional class that makes use of my newly created interface ITarget.

The class looks like this:

public class Poltergeist : Monobehaviour {

    void Start()
    {
        SetNewTarget(Player.Instance.gameObject);
    }

    void SetNewTarget(GameObject newTarget)
    {
        ITarget iTarget = (ITarget)newTarget.GetComponent(typeof(ITarget));
        if (iTarget != null)
        {
            iTarget.vulnerabilityChanged += TargetVulnerabilityChanged;
            target = newTarget;
        }
    }

    void TargetVulnerabilityChanged()
    {
        // Do action upon change
    }
}

What I am not sure of is the GetComponent and typecast of (ITarget) The fact that I have some GameObject, and wish to make use of its (possible) interface, am I really required to make the GetComponent(typeof(ITarget)) and then perform a typecast? Is this how its usually used?

I have always thought that MonoBehaviour was a good example of OOP. All classes that wish to be attached to a GameObject within a scene in Unity must derive from MonoBehaviour, because MonoBehaviours contains important methods and information that allows the script to be considered “Attached” to a GameObject.

That not how I see it. We can agree that a majority of classes will in the long run derive from MonoBehaviour, but not them all. Classes that are independent of the scene, is not required to derive from MonoBehaviour. In a more general sense, you can state that there exist two “Main” base classes. MonoBehaviour and Non-MonoBehaviour. There of course, does not exist a Non-Monobehaviour base class, because there is no information to be shared.

I tend to be bad at sharing written information, so I have created a simple illustration to show what I mean.

shaderop makes the excellent (and frustrating) point that in order for non-MonoBehaviour code to execute it must rely on some call from MonoBehaviour-derived code.