jeudi 28 avril 2022

How to keep the size of the visitor classes manageable?

I've got an interesting issue on the recent project that I can't my wrap my head around. The code below is Java, but the question itself is pretty language-agnostic.

Our application's goal is to store user profile consisting of key-value pairs. The number of key-value pairs is not predefined. The application receives incremental updates on field level, similar to:

// This will increment property `numberOfLogins` of user `123` by 2.
{
    "userId": "123",
    "operation": "increment",
    "property": "numberOfLogins",
    "incrementByValue": 2
}

We also experimenting with multiple storage backends, so we decided to use Visitor pattern:

public interface UserProfileUpdate {
    void accept(UserProfileUpdateVisitor visitor);
}

public class IncrementUpdate implements UserProfileUpdate {
   
    ... // Data fields according to the particular update format
 
    @Override
    public void accept(UserProfileUpdateVisitor visitor) {
        visitor.visit(this);
    }
}

The visitor itself:

public interface UserProfileUpdateVisitor {
    void visit(IncrementPropertiesUserProfileUpdate userProfileUpdate);

    void visit(ReplacePropertiesUserProfileUpdate userProfileUpdate);

    void visit(CollectPropertiesUserProfileUpdate collectPropertiesUserProfileUpdate);
}

So far so good, to hide all the details how updates are processed by different storage backends we can implement visitors:

public class MongoDBUserProfileUpdateVisitor implements UserProfileUpdateVisitor {
    @Override
    public void visit(IncrementPropertiesUserProfileUpdate update) {
       // ...
    }

    @Override
    public void visit(ReplacePropertiesUserProfileUpdate update) {
        // ...
    }

    @Override
    public void visit(CollectPropertiesUserProfileUpdate update) {
        // ...
    }
}

The issue is that visitor classes quickly started to be quite huge and hard to test. To overcome this we have to extract each visit() method to its own class, which leads to:

public class MongoDBUserProfileUpdateVisitor implements UserProfileUpdateVisitor {
  @Override
    public void visit(IncrementPropertiesUserProfileUpdate update) {
       incrementPropertiesUserProfileUpdateMongoDBProcessor(update);
    }

    @Override
    public void visit(ReplacePropertiesUserProfileUpdate update) {
        replacePropertiesUserProfileUpdateMongoDBProcessor(update);
    }

    @Override
    public void visit(CollectPropertiesUserProfileUpdate update) {
        collectPropertiesUserProfileUpdateMongoDBProcessor(update);
    }
}

So my questions:

  • Is there any better way to optimize structure of the visitor in such case?
  • Is visitor pattern a good choice here in the first place?

Thanks in advance!

Aucun commentaire:

Enregistrer un commentaire