1.17.x Proper OO programming in plugins

Discussion in 'Spigot Plugin Development' started by blafexe, Sep 23, 2021.

  1. Hi community,
    during the last days, I've been trying to overhaul my plugin. I wanted to follow common object oriented programming rules, such as minimal use of the static modifier.
    And this is where I got stuck. My plugin has many "Manager" classes, which store data such as custom player classes or areas. I've been desperately trying to get around those Managers, collecting said kinds of objects in a "World" class for instance.
    But this way serializing and deserializing requires the plugin to send the data between more objects, than it used to and accessing is also a lot harder.
    Now, after about 2 days of trial and error I feel like I'm hardstuck on a somewhat wrong/too narrow concept of how object orientated programming should be realized, especially in plugins.
    How do you guys manage or structurize multiple objects in your plugin without abusing the static keyword? I could really use some advice in general.
     
  2. Maybe you could show us an example of something that you don't know how to structure it properly. There are a lot of resources available about code structuring and OOP in general. I guess you could lookup the SOLID principles for now if you haven't already.
     
  3. I am familar with the SOLID principles. But its not really one particular example...
    For instance, I made a class called "Actor", that stores player data my plugin needs, such as learned skills or owned plots. I used to hava a class "ActorManager" that had a static HashMap which stored all actors. Also it stored static functions like "registerActor" or "getActor". Now following all those principles (among which are the SOLID principles) I would need to refractor it, so that I get an object that is not static. But that on the other hand means, that i cannot simply call ActorManager.getActor for example, but have to get the particular Manager instance beforehand and only then I can access its methods. And to get the Managers instance I'd have to store it in my main class for example. Which brings up the question, why I have to avoid making its fields and methods static in the first place.

    To be honest, I cant really explain it that well. What I would need, are some examples how you would go about this. I am not asking flr spoonfeeding code here, I just feel like some theoretic examples woumd help.

    Sorry for that awkwardly long text by the way, I just cant seem to find the right words...
     
  4. That's where dependency injection comes into play. If you are looking for examples, why don't you just check out some public github repos?
     
  5. Yes...
    The point is beeing able to use all OOP advantages, even if you won't in a single case.
    You should always prefer a constant type of software architecture / programming style... at least i would recommend to do so.

    In this specific example you might could have multiple manager instances, like one for every world holding its plot data. And maybe something like a ActorManagersManager holding and organizing all ActorManager instances...

    You always can think of different software architectures to solve a specific task.
    Almost everytime at least one possible solution would require a OOP specific principle... so isn't it better to simply DO it the OOP way in the first place?

    At least for me i can say i personally just like OOP and e.g. never use anything static to access an instance.
    Even if it means that i always need to pass any required other instance to other classes and stuff... but i simply personally feel better doing so, it just feels right to me.
    Maybe because it will be the best precondition, if i decide to rewrite / restructure parts of the software, maybe because i will understand faster what and how i did things in the past after i decide to get back in abadoned projects of mine, maybe because i always expect a future requirement in SW dev when a project grows that wouldn't be possible to implement otherwise... maybe... maybe...

    Whatever might come, one thing is for sure:
    If you did it the OOP way, you will be prepared best for anything.
     
    #5 Michel_0, Sep 23, 2021
    Last edited: Sep 23, 2021
  6. The way I usually store objects, like a manager, are in the class that extends JavaPlugin. Then I add getter methods and pass the JavaPlugin class to the other object constructors that need to access those managers. Static is bad because the JVM won't garbage collect it, and it is harder to test.
     
    • Agree Agree x 1
  7. I checked out multiple repos, Citizens and even WorldEdit to name two of them. I just couldnt get any useful information, as they seem to handle completely different types of data compared to what I want to do. Maybe I need to check out plugins that try to accomplish the same thing.
    I think a good start is storing single instances of the "Managers" I need in my Main class and looking up the proper use of dependency injection.
     
  8. Thats a good starting point, I'll try it this way
     
  9. I'd really love to just use OOP as intended, but I think I am missing some practice.
    For example, how would you structure the following comcept:
    I have an actor class and a manager storing all actors. Now I want to serialize all Actors to json. Regarding the Single Responsibility principle, I would have to create a 3rd class, that can serialize and deserialize Actors from a json file, right? But the actor manager shouldnt instruct the Serializer to do so, beacause its only job is managing actors and not loading files. This way I had to create an other Class that passes the Files into the Serializer which returns the unserialized Objects and only then they get passed to the actor manager.
    This is how I would design it, keeping SRP and Dependency Injection in mind. But is it okay to have such long responsibility chains?
     
    • Like Like x 1
  10. I'd say loading an Actor from disk into memory is considered part of "managing" those instances. You could however, create a new class which serves the purpose of serializing and deserializing a particular object (using generics), though you might be better of just using Spigot's built in way of doing that. Then simply let your manager class handle the rest.
     
    • Like Like x 1
  11. Using dependency injection helps you pass the instance of your plugin throughout your program
     
    • Agree Agree x 1
  12. That looks pretty interesting, I think I'll impliment some of the code.
     
    • Friendly Friendly x 1
  13. Generics is a really good keyword here. I've never worked much with generics, therefore I did not take them into consideration. Previously I has a class with about 10 different methods, everyone of them served the purpose of deserializing different object types. Generics could really simplify this process. Thanks a lot