MartinStone
MartinStone

Reputation: 184

Design pattern for calling specific classes that override an especific method

So I am having a bit of a problem when trying make a class that can be appended subclasses with different functionalities and share a common ancestor without having really big hierarchy trees.

What I am trying to do is make n different sub classes of the Skill interface which are allocated in a list and then called to be execute. All the in Skill are sublclasses of Event (not here).

My problem resides in SkillManager where I don't how to differentiate which Skill it should call and wheter it would be more effect just to create the Skill subclass and execute it right then.

This is my Skill.class interface

    public abstract class Skill
{
    /**
     * Private shortcut so this code doesn't have to be reused over and over to call
     * for cooldowns on the skills
     * @param dp player making the request
     * @return if the command is available for use
     */
    protected boolean canUse(DPlayer dp){
        String name = "skill_"+getName();
        if(!dp.getTag(name).isPresent())
            return false;
        if(dp.getTag(name).get().getRemaining() > 0) {
            dp.sendActionBarCooldown(ChatColor.AQUA + getName(), dp.getTag(name).get().getRemaining());
            return false;
        }
        return true;
    }
    /**
     * Returns the name of this skills, this should also be the same
     * name of the class implementing this interface for initialization
     * propouses
     * @return name of skills
     */
    public abstract String getName();

    /**
     * Executor to be implemented and replace/add functionality
     */
    public abstract void execute();
}

This is my SkillManager.class

    public class SkillManager
{
    private static final SkillManager instance = new SkillManager();
    private final List<Skill> SKILLS =new ArrayList<>();

    private SkillManager(){}

    public static SkillManager get(){return instance;}
    public void registerSkill(Skill s){
        SKILLS.add(s);
    }
    public void registerSkills(Skill... s){
        for(Skill skill : s){
            SKILLS.add(skill);
        }
    }
    public void unregisterSkill(String name){
        for(Skill skill : SKILLS){
            if(skill.getName().equalsIgnoreCase(name)){
                SKILLS.remove(skill);
            }
        }
    }
    public void callEvent(Skill skill) {
        skill.execute();
    }

}

This is Sneak.class the example implementation

public class Sneak extends Skill {

    @Override
    public String getName() {
        return "Sneak";
    }

    @Override
    public void onSneak(DPlayer dp, PlayerToggleSneakEvent event){
        if(dp.getTag("skills_sneak").isPresent()){
//DO STUFF
        }
    }
}

Thanks for the help. EDIT: The Event objects are derived from api calls not from my implementation and contain data about said event EDIT 2: Adding flow diagram. Red is the parts I coded. Blue is part of the structure I am using Flow Diagram

Upvotes: 1

Views: 296

Answers (1)

Guillaume F.
Guillaume F.

Reputation: 6463

There are two major issues in your OOP code:

  • Your callEvent doesn't rely on a unique abstract method for all your events implementations, forcing you to check the type of object to call the right method.
  • You send a cast of the event instance to itself as a parameter, creating a useless dependency with all your event types in the caller.

Use the Command Design Pattern; instead of having onSneak(), onBreak(), etc, just use a common execute() method. In your Skill.class, declare execute as abstract method, forcing you to write its implementation in all the extends. It will be used to trigger the event. callEvent() now needs just one line of code: event.execute(dp);.

You shouldn't send the event to itself as a parameter since, as an instance, it can hold its own properties and is already aware of itself.

PS: You call this optimization in your question, but this is not really optimization. Fixing the architecture will hardly make your code faster. It will, on the other hand, make it easier to read, maintain, and more reliable. Beware of premature code optimization.

Upvotes: 1

Related Questions