Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

I have the tendency to overuse singletons...

Discussion in 'General Discussion' started by Luiz_Thiago, Aug 10, 2017.

  1. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    The thing is about architecture and program design is that you can't really enforce your own pure style if >90% of the code and design isn't yours to begin with. For a start you've got the Unity engine to contend with, which already uses several managers, singletons, and so on. Next up you have the Unity monobehaviour pattern, components and so forth.

    This means chasing the perfect architecture is like an architect only having control over the toilet area in a large tower block. You might make it flush super well but only in a small part :D

    ...This is to be expected I suppose. With UE4, your code pretty much has to be epic's style or you might as well not bother.
     
    DouglasPotesta and Kiwasi like this.
  2. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    I think the purpose of pursuing an ideal architecture is not to enforce this or that style over the entire software stack, which would be impossible except for toy projects which do not rely on any external libraries or frameworks.

    Rather, it is because people want to make it sure as much part of the codebase that they need to write and maintain themselves to be in a style that they feel the most comfortable with.

    I don't think everyone should be a software architect to develop video games, but at the same time, I believe that if they understand some of the principles of software engineering, it would help them write code that is more efficient, readable, and easier to maintain.

    So, while it is entirely possible to write cool games without thinking much about writing a manageable code, I don't think it 's meaningless to discuss the subject, as it can still benefit those who care about code quality, and coding is undeniably quite an important part of making video games.
     
    angrypenguin and AndersMalmgren like this.
  3. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    My point was more along the lines of why are we resisting using Unity's own methodology?
     
    Kiwasi likes this.
  4. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Offcourse maintainable code is more important in a team environment, but its also important in a solo project.
     
  5. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    For my part, it's because while I see many questionable design decisions in Unity's API, I find no reason to mimic them as I'm not developing Unity myself, but a game that runs on top of it.

    It's less of actively trying to do things differently from the way it is done in Unity's own API, but more of just trying to keep the way I'm used to even though they clash with each other from time to time.
     
    Last edited: Aug 15, 2017
    AndersMalmgren likes this.
  6. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    Are you talking about making the class static? Because then you'd lose the ability to leverage the benefits of deriving from MonoBehaviour and attaching to a GameObject.
     
  7. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    You build an abstraction around it as much as possible. Our domain code more often than not use abstraction then direct using Unitys SDK. It has lots of benefits, first, Unity SDK is pretty bad if your a C# deveoper and used todo things the C# way. Second, Unity can change things and you only need to change the abstraction implementation not the consumers of the abstraction.

    Real world example,

    Code (CSharp):
    1. public class RollerDelayedBlowbackSlide : RotatingBolt, IPhysicalHandCollidable
    2. {
    3. ...
    4.         public void OnCollide(NVRHand hand, Collision col)
    5.         {
    6.             if(isClosed || (!isClosed && !isOpen))
    7.             {
    8.                 var force = Vector3.Project(col.relativeVelocity, SlamDirection.up);
    9.  
    10.                 var angle = Vector3.Angle(SlamDirection.up, col.relativeVelocity);
    11.  
    12.                 if (angle < 60 && force.magnitude > 0.25f)
    13.                 {
    14.                     ReturnBolt();
    15.                     hand.ForceGhost();
    16.                 }          
    17.             }
    18.         }
    19.  
    20. }
    Our framework makes sure all IPhysicalHandCollidable gets the collsion event, this also has the benefit of getting around the restricion in unity that only the behaviour with the rigidbody can have the collsion event

    The above code does this :D
     
  8. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    I assumed you wouldn't use MonoBehaviour derived classes as a static singleton, because it'd be tying up the lifecycle of (shorter lived) platform managed objects to a global classloader.

    It's considered a bad practice too, because it makes code prone to memory leaks and harder to keep track of objects.

    Anyway, if you want to write a global MonoBehaviour services, what about just writing them as ScriptableObjects? I believe it to be a more Unity friendly way to solve such a problem, and it doesn't violate that many design principles as staticallymanaged MonoBehaviour singletons.
     
  9. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I tend to prefer this over exposing an instance variable. It might be a singleton on the inside, but from the outside its pure static.

    I find it tends to be more robust then a simple singleton.
     
    frosted likes this.
  10. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    I guess it's a case of whatever works for you. I like using Singleton Monobehaviours for the small(ish) mobile games I make. I don't recall any issues that made me regret using them. Plus they allow me to attach extra development side components which make my life easier when getting things working/debugging.
     
  11. steego

    steego

    Joined:
    Jul 15, 2010
    Posts:
    968
    Singletons work great for this, until suddenly they don't anymore, and then you'll have a lot of work on your hands to refactor your code. For example if you should ever want to write unit tests, singletons can be a major pain.

    But if it works for you, I'm not here to discourage you from using them. I don't believe in preaching for this or that, but I think as the scope of your projects grow, you will learn from experience when not to rely on a singleton.
     
  12. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    The problem with this, and what I was trying to get at when I said nobody cares if you can maintain your own code, is that a large reason why we use standards and design patterns is for the benefit of others.

    Your own code you will largely memorize the larger structure and where everything is. Other developers don't have that luxury.

    Singletons are just one of many details that will tend to make code less readable and more difficult to work with.

    In the context of working at a company that's been around a while, the productivity impact of writing good code is rather large. Good companies will in fact discourage staying on one part of the codebase for any length of time. The worst thing you can have is a part of your codebase that only one person can work on effectively.

    And trust me, these companies do care about stuff like this. It's ok to say yes I know such and such isn't the best way, and here is why I did it in that case. And that's perfectly fine in the right scenario. But saying well it worked for me, will be a huge red flag. Because they are looking for developers that write code in the context of how easy is it for other developers to maintain.
     
    jk15 likes this.
  13. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    It's hard to tell if anyone who's anti-singleton has ever actually used a singleton properly, since no one ever gives examples. Let's be real, all terrible design decisions are a bitch to refactor. It's just that with the singleton, you can point specifically to it and state that that was the root issue, whereas most terrible designs are an issue of complexity finally becoming untenable, with no one thing totally to blame.

    The unit test thing is curious to me, because chances are that any singleton that interferes with a unit test is probably a bad singleton. Nothing outside a singleton should care what the state of it is, barring something that's only supposed to be global state like an expanded time class.
     
    hippocoder likes this.
  14. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    Personally, I don't believe singleton to be an evil pattern per se. It's just that the original purpose of the pattern is to ensure there to be only single instance of a class at any time, not to give people a shortcut to manage class references.

    And aside from that, it inevitably introduces tight couplings to the project so people began to move away from using the pattern to better alternatives. But if you are sure that there won't be any need to extend or replace such singleton classes later, and they do in fact represent some concept that ensures a unique existence, it's alright to use a singleton pattern.

    DI framework is great and I'd personally recommend it to anyone, but it's far from saying it's mandatory to use it to write a good code.

    As to unit testing, I don't think many people will suffer from such a problem when using singletons in a game project because, well, unit testing doesn't seem to be a widely used practice among game developers yet :p

    But there are some legit reasons why singleton pattern is considered problematic in that context, especially in business applications.

    Usually, proper singletons are used to ensure that some processes which incur much overhead to be only performed once at the initialization of the system, such as setting up a connection pool, for instance.

    In general, things like that have little to do with unit testing, because unit testing is about quickly checking the sanity of an isolated component, rather than ensuring things like remote connections to a production database to work properly.

    But you can't simply ignore singletons in unit tests, because other classes that should be tested usually rely on them. In general, singleton becomes problematic whenever there's a need to switch implementations because of tight couplings it introduces to the classes that use them.
     
    Last edited: Aug 15, 2017
  15. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    This kind of logic is why I drink at noon. There is nothing inevitable about coupling a singleton. I'm just going to bitch for a second here, because there's a certain proclivity programmers get into when adopting new paradigms where everything not using that paradigm must then absolutely fall into the one or two traps the paradigm inherently prevents. The meme de'jour with the DI/IoC crowd is tight coupling. Now, there are two main instances where loose coupling is paramount, and they are the theoretical academics and the agile developers whom have no idea what their program will even do in five years. If you're a hungry game developer, pristine code is not a luxury you get.

    Now to get back to singletons, a theoretical singleton facing a theoretical problem that will theoretically change will always be inferior to the improbability protected programming panacea. My problem here is that since this entire discussion is about ephemeral cases, my response to every one can be to shout "it's because you're doing it wrong, dipshit", then laugh maniacally with Statler and Waldorf on the balcony.
     
  16. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    I presume that either you have misread my post, or you don't understand what 'coupling' is in the context of software design. Unless we are talking about singleton factories, singleton pattern does introduce a very tight coupling between a service and other classes that depend on it, by definition.

    The only arguable part about the subject is whether or not such a coupling is undesirable and if so then how much. And to that question, I already said that it'd depend on the circumstances because in some cases, using a few singletons won't cause too much trouble at all.

    The difficulty in learning or discussing software design is that it's not easy to understand a problem unless you are already familiar with the solution. But that, by no means proves that such problems do not exist, or are not important.

    People happily created and played video games back in the 80s, when there was no concept of advanced 3D game engines. But that does not 'prove' that the problems such a platform like Unity tries to solve are imaginary or academic.

    Rather, it only proves that those who do not understand the implication of such a problem have not experienced working on a complex enough software project where such things really matter.

    And actually, reducing couplings between components in a software system has been arguably one of the most important problems in software engineering field. Ever wondered why C# supports interfaces? It's actually to solve that specific problem you claimed to be non-existent, just to take a single example out of so many.

    If you find yourself successful at creating games without bothering too much about writing manageable code, then good for you. I don't see any problem with that. Games and those who create them become successful for different reasons, and not all of them require top notch programming skills to do so.

    But the same can be said of any other aspects of creating video games too. Claiming a problem of reducing couplings in a game project is imaginary is like saying that things like global illumination or physics based rendering are non-existent problems in creating games, simply because you have been highly successful in selling Minecraft like games where such things don't matter at all.
     
    jk15 likes this.
  17. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Or the third option where I could be one of the rare computer science majors without my head up my ass... But it's always possible, and probable, that my ability to parse terminology is hampered by my understanding of this pesky thing known as the English language. I must be eternally cursed to roam the lands, suffering under the yoke of cruel vagaries.

    So if I just wrapped a singleton's public functions in an interface, and searched by interface type, wouldn't that mean your entire argument is bullshit? Although I don't imagine there's any way to guarantee a single instance of an interface. Oh no, now I need a more complicated service locator than FindObjectOfType. Woe is me.


    What I care about far more than coupling is dependence. A singleton with only one direction of dependence is rarely going to create a problem. Let's take an audio manager which largely, if not only, receives outputs from the rest of the codebase. It's main job is grooming and mixing output. What would be wrong with it being a singleton? What would be wrong with it's main usage being one line of code that does nothing to the surrounding code?
     
    Peter77 and Kiwasi like this.
  18. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    Steady on no need to resort to throwing null refs at each other.
     
  19. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    You are fighting a straw man here. I've never said one should always use interfaces or service locators.

    The argument has started when you said that singleton pattern doesn't introduce any couplings, and/or that they are only academic/ephemeral/imaginary problems, and I cannot see how what you wrote above can defend that position.

    The potential problem of singleton pattern has little to do with the direction of reference.

    But before saying any further, I'd like to emphasize it's a potential problem, since I repeatedly said in this thread that using singletons itself is not a problem as it is, because it would depend on the intention and the scope.

    Anyway, the problem with singleton pattern in that regard is that you cannot easily extend or replace such a service without also affecting other classes that depend on it (hence, it introduces a 'tight coupling' between them).

    Imagine that audio manager service in your example was some sort of a library type API. And if someone wanted to extend its functionality, say adding features for some specific hardwares, then how can one be supposed to achieve that?

    You cannot really switch the implementation of the audio manager in this case with a different one, without also changing every lines of source code that references it, like "AudioManager.Instace", and I don't think I should explain why a library that cannot be extended without changing its source is a badly designed one, if you majored in computer science as you said.

    And the same can be said of the case when you write unit tests for that class. It won't be too difficult to imagine why you wouldn't want your test cases to actually play audio during the test, or why you wouldn't want tests to fail when it doesn't have all the references to actual audio files.

    Again, the coupling between the audio manager implementation and various classes that references it prevents you to replace it with a dummy or mock implementation during the test, which is quite a common, and recommended practice actually.

    Of course, I'm not saying that everyone always design their game code like it's some sort of a framework, or they should always write unit tests for it. So, that's why I said it all depends on the circumstances.

    But on the other hand, it's not hard to imagine why such kind of limitations could be a real problem in many different situations. So, your claim that it's only an academic or imaginary problem is quite far from the truth.
     
    Last edited: Aug 16, 2017
    steego likes this.
  20. steego

    steego

    Joined:
    Jul 15, 2010
    Posts:
    968
    Let me just say that a lot of games are made like this today, for example a lot of casual mobile games. They will have a lifespan of maybe 5-10 years, with an ever growing codebase and constant turnover of developers, so suddenly these things start to matter a lot more. Not to mention that Unity is used for a lot more than games as well, so claiming it is irrelevant for Unity developers is simply untrue.

    Just because it doesn't matter in the games you make doesn't mean you can extrapolate to all games.
     
    jk15 likes this.
  21. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Doesn't even need to be for Mobile, look at DayZ been in early access since 2013, still in development, pretty sure the reviews would have looked better if the game was developed a bit agile
     
  22. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    My code used to be quite good, standards driven and agile. I released very little. I switched to making something "just F***ing work" and released a lot of stuff.

    These days I use a secret technique. It scales based on how many members are in the team, if it's a team, and how much money there is available to develop with, plus time constraints. It's called

    Common Sense

    The fact is if you have a good problem like success then this is actually the right time to engineer it properly, not before, unless you're a cog, who's job is already servicing a successful project.

    If it's one guy, he can only afford to barely get it out of the door so the solutions really should be about getting it out of the door. Singletons, bad patterns, everything bad - just do it.

    If you're minted from a successful project then you can freely hire many cogs who will happily fix it all for you.

    That's my modus operandi, and it works for me. Although it has to be said, my code really isn't that bad because Unity by default is pretty open to extension if you use their methodology instead of replacing their methodology. Because by default it's designed from years of noob feedback and everyone wants different things.

    So maybe a lot of people are solving things that are solved in some way or other.
     
    frosted, Kiwasi and GarBenjamin like this.
  23. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Let me reiterate a point I've stated: EVERYTHING is a problem if the assumptions it's built on are pulled out from under it. You can fool yourself into believing that one methodology is less painful than all the others, but they aren't. Just about every programmer comes to think that their one method of doing things prevents all the issues from happening, when it's more likely they themselves are what prevent the stupid S*** from happening.

    But the example I used wasn't a part of a library and actively separates the codebase from the audio implementation... Whatever, let's just roll with it. So we've got an audio library, it's got a singleton manager, and let's assume that no one who made the library ever cared that someone would want to add additional outputs. Let's also assume you have done nothing to separate your primary codebase from the library, so that there are calls to it all over the place. Is it starting to sound like you made several terrible mistakes?

    Also, you do know that you can inherit from and override a singleton, right? If you need a dummy for a unit test, it's not hard to do. Hell, you can go a step further and make a debug singleton that the unit test creates. Depends on the implementation I guess, but it's not impossible.

    What DayZ needs is about fifty more developers so they can actually get the game done. The game was massively overscoped and probably beyond any of their expertise at the time. Agile wouldn't have helped a thing.
     
    frosted likes this.
  24. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    I used to be a fanatic about this stuff. But over time (and really just reached that point starting a few years ago) basically came to the same conclusion as @hippocoder

    I stopped focusing on designing beautiful systems and layering on abstraction. I took it to an extreme though where I even was happy working entirely procedurally with no oo architecture.

    It definitely made development much faster and reduced the problems / complexity a lot in the process. Over-engineering is definitely a thing. And it is what academics most often do.

    Really I think it comes down to what @hippocoder was saying... what is your focus? Your being any of us. Is it design and code great systems. Or is it to design and build great games?

    Much of the stuff the modern best practices "solve" has been blown out of proportion in the marketing of these best practices.

    Tight coupling is one of them. There are extremes where it can be a disaster. That is true. But most examples are such tiny problems as to not be a problem at all. If I wanted to swap in a different singleton AudioManager for testing it is this hard to do so: Rename the existing AudioManager class (maybe just add "Old" on the end of the name), drop in the new AudioManager class. Done.

    I get that it might not be seen as a proper workflow but it is a very efficient and very valid approach.

    Common sense would tell me that yes the method names and signatures need to be the same as the original. Here we're talking about simply replacing the implementation which I take to be referring to the innards and not changing the public interfaces (not those kind of interfaces... I mean the methods) to the implementation.
     
    Last edited: Aug 16, 2017
  25. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    For released products that need to expand or be refactored, Unity actually has features for dealing with this. So if you designed it badly and wanted to change a whole lot of stuff over time, it would not be that bad if adhering to Unity's own design pattern with https://docs.unity3d.com/ScriptReference/Serialization.FormerlySerializedAsAttribute.html

    And there are other strategies. But like I said, it's a good problem to have. If someone's crappy old bodge up is still around 3 years later, they're going to be making good money from it, the kind of money that makes hiring people not a problem, assuming no signs of madness.

    If it's someone who wants to make a career out of it and never expand, I think we have a good reason to be architecting code well at the start, but this only increases start risk.
     
    Martin_H and GarBenjamin like this.
  26. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    haha, you know that the Arma engine is based on Operation Flashpoint from 2001? Same bugs are still present. Biggest problem with that game is the quality of the code. I have seen examples where they fix a bug on one weapon or mechanic but the bug remains on another place because the code is just duplicated all over the place. Flashpoint/Arma/DayZ is a perfect example what happens when you do not think about maintainability. Still love the games, thats why its so sad they weren't written better.

    Here is a system that I have made thats lived since 2012, still maintainable
    https://github.com/AndersMalmgren/FreePIE
     
  27. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    Sorry, but you are again sparring against a straw man. Who said that singleton pattern is a problem no matter what, or DI/service locator/anything is the one and only good method that everyone should use instead?

    I've never said anyhing even vaguely similar to that, and I've been reminding you of the fact in every one of my replies.

    If you want to discuss about something I actually said, then please stay on the subject. But if you just want to keep arguing against exaggerated claims from an imaginary programmer who clings to some fancy methodology without understanding it, please stop quoting my post, since I never said anything like that.

    Reminder: your initial claim was that singleton pattern does not introduce any tight coupling, and it's only an imaginary problem. Remember that?

    Now if you agree that in case with library type APIs, it could be a real problem then you are simply agreeing with my position, as it's been nothing more than singleton pattern can cause trouble in certain - not all - situations.

    And separating calls to a singleton from the code? How that's even possible without introducing something like a service locator or even DI of some sort which you seem to so stubbornly object?

    The very problem with singletons is that you need to directly reference one specific implementation instance of the class (like AudioService.Instance). And the very reason why people use things like service locator pattern or DI is to solve that specific problem.

    So, if you are saying singletons are not a problem in any case, since you can easily extend them if you like, please show me how to do that without introducing yet another design pattern or framework which you seem to think all just BS that only clueless programmers care to use.
     
    Last edited: Aug 17, 2017
  28. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    On a side note, I've always wondered why it seems to be almost considered a sin to talk about code quality in this forum. Every time I stumble upon one of such discussions, I see people saying that you should not care much about such stuffs because all it matters is getting things done, and such.

    Is writing bad code somehow more productive than writing good one in Unity? No, I don't think so. I might agree that over engineering is a bad thing, but it applies the same to non game projects as well.

    And we are only talking about singletons and service locators here. Are singletons any easier or faster to write than service locators? No, probably it'd be the other way around. And honestly, both of them are so trivial to write that it wouldn't affect your project schedule in any meaningful way one way or the other. Even DI is not at all difficult to use, if you already know the concept.

    Will I become more productive in developing games, if I just forget everything I know about design patterns, DI frameworks and anything that's related to software engineering and start writing code like I was a professional artist who just learned how to program instead? I doubt it.

    Probably all it boils down to is that it takes time to learn these things if you are not familiar with them already, and it's certainly true.

    And I believe it's up to each individual to decide how to invest time to improve one's skill. And I don't think there's any good or bad answer to that question. If you cannot say something like, "oh, you should never worry about advanced modelling stuffs, because there are plenty of successful games that use only low poly models out there", then you probably shouldn't tell people to stop caring about code quality simply because you have created great games without worrying about it either.

    I agree that it's entirely possible to create great games without top notch programming skills, but if someone decides to take some time to learn these things, it won't make them any less productive game developer, if not much better.

    I've never accused anyone of not using this or that design pattern or methodology here, and all I want to do is to discuss pros and cons of using singletons which I believe to be the topic of this thread.

    And I believe people should be able to discuss such matters here, without continuously being told why they shouldn't care about the code quality because it does not matter, or being accused of blindly adhering to this or that methodology simply because they mentioned them.
     
    Last edited: Aug 17, 2017
    jk15, steego, Peter77 and 1 other person like this.
  29. GarBenjamin

    GarBenjamin

    Joined:
    Dec 26, 2013
    Posts:
    7,441
    @mysticfall I hear what you're saying. I think basically people are just discussing the subject but each person has a different frame of reference.

    I'm not saying you or others adhering to such practices is wrong or "dumb" or anything like that. Just saying that for me personally I threw all of this stuff out because it got to the point where it seemed like I was spending as much, if not more, time working on building layers of abstraction and working on the problem / end goal indirectly than I was spending time working on the actual problems / goals.

    When I threw it all out and started from scratch taking a very simplistic approach it was incredibly refreshing. Everything "just worked". I do think probably a big part of that is because I had spent a lot of time previously using DI and other decoupling strategies etc... basically I mean I was very aware of the pitfalls to watch out for and when I use a singleton manager or whatever it is a very intentional decision knowing the dependencies that I am creating.

    Actually, I think that is what @RockoDyne was getting at when he mentioned something about it being the programmer themselves actually doing it not so much the use of any specific best practice.
     
  30. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,526
    To me, "code quality" is one contributing factor to "getting stuff done". In so far as writing better code helps me get stuff done, heck yeah I'll do it and advocate that others do the same.

    You absolutely should care up to the point where your code is good enough, after which there's little to no additional benefit from making it better. The tricky bit is deciding what "good enough" actually is, and the best ways to go about getting there. And that's probably going to be different for every team and every project.

    On the topic of Singletons in particular, I don't think they're as bad as they're often made out to be. That said, I think that the idea of using them to provide global access to something is misguided. That's not the point. If it were the point it'd be called "The Global Access Pattern" or something. The point of a Singleton is to make sure that exactly one of a thing exists, and the use of a single, global access point just happens to be a part of how that is enforced.
     
    Peter77, Kiwasi and mysticfall like this.
  31. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    I would actually be interested in a discussion of code quality in regards to working with Unity and its systems.

    But inevitably these discussions end up being about trying to force Unity to work to some external paradigm that doesn't fit with how Unity is built. DI is a good example. I've seen all sorts of crazy code back flips, just to get Unity to work with DI. (And completely ignoring the fact that the inspector is a DI system). And then once the whole thing is done, it still has to be enforced by convention, and you've lost half the benefits the paradigm provides in the first place.

    If someone wants to build these patterns in a way that works naturally with Unity, I'm all for it. But ripping and replacing what makes Unity great in the first place normally isn't worth the effort.
     
    ippdev and frosted like this.
  32. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    I wouldn't call design patterns as something 'external' to Unity, because they are even language agnostic in most part, so they can be used in any software that is written in an object oriented language.

    I admit that some of the common practices might collide with Unity, especially with the restrictions regarding serialization and the use of MonoBehaviours. However, I think they are more of an exception than a rule, and those patterns discussed in this thread are not really affected by the fact that they are applied to a Unity project.

    As to DI, I can understand that how it might feel alien to the usual workflow that Unity provides. But from what I've experienced so far of Zenject, I found it fit quite well with the rest of the platform, and I especially liked how it enables me to mix and match MonoBehaviours and plain C# classes freely, and it also work quite well with Prefabs or ScriptableObjects, so I haven't found any serious issues where it clashes with the way things are supposed to be done with Unity yet.

    And DI is quite a popular concept among programmers in other fields for a good reason. So, while I wouldn't say that everyone should learn and use one of the DI frameworks in their Unity projects, I don't see any reason why they shouldn't even give it a try and see for themselves if it'd work for their use.
     
    jk15, AndersMalmgren and angrypenguin like this.
  33. Player7

    Player7

    Joined:
    Oct 21, 2015
    Posts:
    1,533
    I like singleton pattern, all the best of static without all the associated problems of using and interacting directly with it.

    So I have 3 main singletons.. Gamemanager, Ugui, DevConsole

    The GameManager is the main one that handles scene cameras, scenes loading/unloading, audio management, game settings etc

    The Ugui one is for multiple canvases that are split into Options, MainMenus, InGame, Debug hud, etc so its pretty much a manager for all that and anything from other scenes that might need it.

    I don't see singletons as a problem at all, problem is more about getting things setup and dealing with the complications as the project grows, like with multiple scenes being loaded/unloaded and taking care of that so nothing else tries to access what might be inaccessible at some point.. so structuring it with that in mind, I still use additional singletons within particular scenes for object pooling, self contained for just things in that scene.. fairly happy with the setup.
     
    Martin_H likes this.
  34. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,739
    why i really hate work with ue4, its not found of you finding your own creative way to solve architecture problems. Well that on the docs for the C++ api are rubbish and the community seems to think having source code is a good excuse for bad docs.,
     
  35. DouglasPotesta

    DouglasPotesta

    Joined:
    Nov 6, 2014
    Posts:
    109
    There is a lot you can get out of unreal if you do it their way though. With unity you are free to do anything which is so freeing. It is truly a sandbox of an engine. However it's hard to make a castle out of sand. If you don't plan carefully, your features can collapse in on themselves, leading to rewriting a lot of code.

    However in Unreal engine, if you follow the proper workflow, you will end up with a lot of systems falling in to the right places. The annoying workflows in unreal engine are done a specific way so that you can implement other systems. You may not need all those systems though, so there can be some wasted time.
     
  36. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Does Zenject support lifetime scopes? Does it support something like

    Code (CSharp):
    1. container
    2.    .Bind<IMyService>()
    3.    .To<MyService>()
    4.    .InSceneScope();
    And that zenject takes care of lifetime, then I would be sold :D
     
    mysticfall likes this.
  37. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    It sure does :) Like many DI containers, Zenject has a notion of a context hierarchy, which consists in project, scene, and game object contexts, mainly.

    And your example looks very similar to the actual API that the framework provides.

    It's quite well explained in the documentation, so you might want to read it here:
     
    AndersMalmgren likes this.
  38. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Cool, will investigate! Thanks
     
  39. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    The question I'd have at this is why not simply use a natively scene scoped object - ie: monobehaviour.

    One of the concerns about these kinds of systems is having to redefine everything and obscuring how things fundamentally function in the environment.

    That one developer might be more 'accustomed' to working with scoping provided by a DI framework, all you end up doing in reality is renaming and remapping existing functionality to a new set of names and idioms.

    The danger in doing so is that you fail to really learn the intricacies of your environment.

    I prefer writing closer to the base environment, not only because I believe it to be more efficient (both in terms of performance and in terms of development time) but because doing so forces you to learn to be truly proficient in how your environment functions.
     
  40. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    You can change to .InSingletonScope(); without changing a line for example. You should always struggle to write a architecture that abstracts the API (Unity in this case) and that supports your domain better.
     
  41. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    In Zenject, a scene context is actually just that, a single MonoBehaviour component on a game object in a scene. So, setting up is not much more of a hassle than the normal way of things, except for the fact that you'd write an 'installer script' that declares such binding statements, rather than drag each components in editor to respective properties in which they need to be set. It's actually more convenient that way too, especially when there are many such properties.

    But the real benefit with this approach is that it makes the whole structure moe flexible by moving the task of instantiating of resolving such dependencies out of each components that needs them, and putting it into an easily manageable metadata component.

    Suppose you have some service that is referenced by many other components. You might begin with writing it as a component attached on a game object in a scene. But what if you want to convert it into a prefab or a scriptable object later, or find that you need to read it from an asset bundle?

    And how many components need to be changed if you want the player to choose among a number of slightly different implementations of that service, depending on in-game settings?

    What if you made an NPC prefab and instantiate it every time they appear on screen, but later found that it degrades the performance too much so you want to replace it with an object pool? Or what if you want the service to be instantiated from a prefab, but only once so that every other game objects that depend on it reference a single instance of it?

    And what if that service, or other components that depend on it also have other dependencies themselves that might change later in similar ways? What if some of them don't even have to be a MonoBehaviour, so you don't want the hassles with creating a game object and attach it every time you need them?

    Things like that can happen all the time and a DI container is usually used to solve such kind of problems. With Zenject, for example, you can simply change a few lines in your installer component when such things that were mentioned above happens, rather than changing every components that depend on the changed service.

    I agree with you though, that one might still need to know how it works under the hood, because things like a DI container is there to make it easier to do certain things, not make it possible to do it without actually understand how it works.

    But I think the same thing can be said of anything which involves some kind of an abstraction, and even Unity can be said to be one of such example, as it provides many useful features to developers by shielding them from the details about low level graphic APIs or the player's hardware, for instance.
     
  42. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    I would disagree with that entirely. In fact I think it's one of the most counter productive habits that most developers indulge themselves with.

    To each their own - but this is absolutely incorrect in the vast majority of actual use cases and a major problem with how most software is written.
     
  43. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    The thing is - if you are competent in the environment - these changes are already trivial to make.

    The other day I changed a component from being something loaded indirectly in a separate scene loaded additively, to something composed on the fly in code to something prefabed and referenced to something loaded at runtime from prefab.

    Those changes were something that required almost no thought or effort, and very minor change to actual code - the reason those changes were so easy is because I've become competent working in the Unity environment.
     
  44. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    In a large team project that means 100 ways of doign the same thing, alot of duplicated code etc. A architecture that supports the domain code is always a must. My OnCollide code above is a good example.
     
  45. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    I know that it's really easy to change how a component is to be instantiated, either from a prefab, resources, an object pool, or whatever.

    But it can become quite a hassle no matter how proficient you are in Unity, if the said component is referenced by many other components already. You need to reassign their properties at the very least, if one of such changes happens.

    And just like how using prefabs or asset bundles becomes easy once you get proficient with them, the same thing can be said of using a DI container. At first, it might feel unnatural or cumbersome to work with it. But once you understand how it works, it won't require you much efforts or thoughts to use it, just like what you said about the core concepts of Unity.

    I remember how number of people here argued before, that you shouldn't do constant refactorings because it will break too many things when the project is in an advanced stage. But the kind of structural flexibility that DI container grants is specifically the kind of a remedy to such a problem. And being able to improve your code without affecting too many existing components is certainly a good thing, if all you need to do is just getting used to a concept like a DI container.

    Again, I can't really force anyone to use Zenject, or any other DI containers, and I don't intend to even if I could. But when a tool is as widely used among other developers as DI frameworks, I'd like to encourage people to give it a try without any bias at least, maybe just to see what all the fuss is about.

    Not everyone is such a conceited programmer who likes to preach this or that new methodology is kind of a silver bullet that everybody should use, simply because that makes him feel like he knows better than everyone else.

    With DI, I believe so many people are using it because they actually find it quite useful, not because it makes them feel better of themselves :)
     
    Last edited: Aug 18, 2017
  46. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Certainly. A great deal of my code did need to interact with that functionality mentioned in my example. It was still very, very simple to do.

    The fact is - changing code is not a problem in most cases unless you're working on a public facing API or in a more complex team environment, where changes can negatively affect coworkers.

    Small teams can be far more efficient than larger teams, and to ignore those efficiency gains by addressing problems that small teams never have (by virtue of workflow) is a tangible loss.

    If you want to use DI containers, cool, but to assert that DI containers provide a better process in Unity is IMO deeply mistaken. Maybe these are habits that you've grown so accustomed to that you prefer the approach regardless of efficiency, I respect that.

    What works best for different people will vary, and it's worth sharing approaches, results and real reasoning. That's different than arguing that "this is the correct way".
     
  47. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Also - for the record - I've mostly dumped singletons for purely static classes.

    Singletons were never an ideal, they were always nothing more than a workaround for lack of static inheritance.

    The reason to use a singleton in unity is because you want to modify in editor, so you need some backing instance.
     
  48. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    I find the abillity to refactor my code easily to be very valuable even when I work alone on my hobby project. And even I admit that I'm quite accustomed to use a DI container myself, I fail to see how it can incur so much cost as you seem to imply.

    If you say changing number of properties of existing components from time to time to be a 'trivial' task, how come attaching a single MonoBehaviour component to your scene and write a single line each time you need a new component to be much more time consuming or complex than that?
     
  49. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    I guess the point is: if it's trivial either way - why use any 3rd party api? I think we'd both agree that DI containers are not needed to provide an ability to refactor.

    Again, I've both used DI containers in Unity and outside of Unity. I've also learned to really use the component model that Unity provides out of box, after becoming very fluent with the later, I see no real reason to use the former for my process.

    I would argue that by providing implicit context for monobehaviour components that are used in editor, you actually end up creating more brittle components that are harder to use, understand and combine in editor. Monobehaviours which are actually used in editor are far, far easier to work with when all dependencies are explicit.

    A big part of this does depend on your workflow though - how much of your functionality is configured in code, vs how much is configured in editor. There are meaningful differences.
     
  50. mysticfall

    mysticfall

    Joined:
    Aug 9, 2016
    Posts:
    649
    I don't see it to be any different than using other scripting assets from the store. Maybe I'm biased because I'm accustomed to see at least 50 different libraries in any single project, but I'd be surprised if it is considered a bad practice to purchase and use any scripting assets in a Unity project.

    If it is ok to use an external library to parse JSON, for instance, why we can't use a DI container which is readily available as a Unity asset?

    And I agree that a DI container is not a requirement to solve refactoring problems. But reducing points of a change has always been considered crucial to make your code easier to refactor, and DI containers provide a very good, if not the only, option to achieve that.

    But now we came to a full circle, so the question really is what is more time consuming - those scenarios that I took as an example in my previous post, or setting up and use a DI container.

    If you think that the latter to be much more so, I believe it's time to agree to disagree, as it seems to be quite a subjective matter on which we have so fundamentally different opinions.
    I actually agree with that. Even though Zenject doesn't forces you to do it any differently from the 'normal' way of doing things, it at least allows you to adopt a more programmatic approach which I found to be another reason to like the framework personally.