Potemkinsches Dorf

Manchmal macht es Sinn komplexe Objekte durch Attrappen zu ersetzen. Nach aussen hin sehen diese Attrappen aus wie die echten, schweren Objekte, tatsächlich sind sie aber leicht und einfach.

Sehen wir uns das am Beispiel des automatischen Testens einer Managed Bean in einer JEE-Anwendung an. Wenn wir hier gegen eine echte Datenbank testen würden, wäre der Aufwand hoch. Jeder einzelne Testfall müsste sich in der Datenbank einloggen, Testdaten einfügen, seine Tests durchführen, dabei vielleicht umfangreiche Lese- und Schreibtransaktionen absetzen und am Ende diese Daten wieder entfernen. Abgesehen vom Verwaltungsaufwand würden diese Teste hohe Durchlaufzeiten haben, was wir nicht wollen. Der zusätzliche Wert, den die Verwendung der Datenbank an dieser Stelle bringt ist relativ gering. Trotzdem: Wir wollen unsere ManagedBean testen ohne sie zu verändern.

Dazu verwenden wir für die Tests einen Platzhalter für das Objekt, das für uns die Datenbankzugriffe erledigt. Dieser Platzhalter implementiert dasselbe Interface wie unser echtes Datenbank-Zugriffsobjekt, aber seine Umsetzung gleicht der eines Potemkinschen Dorfes.

Wir demonstrieren das jetzt an dem DataAccessObject für unseren RESTeasy und Wildfly Beitrag. Dazu erstellen wir statt einer realen Datenbank eine Klasse, die die Objekte im Speicher anlegt.

Das kann so aussehen:

@Named
@ApplicationScoped
public class InMemoryUserDatabase {
    private Map<Integer, User> users;
    private int sequence = 0;

    public InMemoryUserDatabase() {
        users = new HashMap<>();
        addUser("Joe Sixpack", "joe.sixpack@example.com");
        addUser("John Doe", "john.dow@example.com");
        addUser("Jane Roe", "jane.roe@example.com");
    }
    public User addUser(String name, String email) {
        User user = new User();
        user.id = ++sequence;
        user.name = name;
        user.email = email;
        users.put(user.id, user);
        return user;
    }
    public Map<Integer, User> getUsers() {
        return users;
    }
}

Hierbei haben wir der Datenbank den @ApplicationScope gegeben, so dass die Daten während der gesamten Laufzeit der Anwendung erhalten bleiben.

Damit implementieren wir unser UserDao:1

@Named
@RequestScoped
public class InMemoryUserDao implements DataAccessObject<User> {
    @Inject
    InMemoryUserDatabase userDatabase;

    @Override
    public User findById(int id) {
         return userDatabase.getUsers().get(id);
    }
    @Override
    public List<User> listAll() {
        List<User> sortedUsers = new ArrayList<User>(userDatabase.getUsers().
values());

        Collections.sort(sortedUsers, new Comparator<User>() {
            public int compare(User lhs, User rhs) {
                return lhs.id - rhs.id;
            }
        });
        return sortedUsers;
    }
    @Override
    public User add(User user) {
        return userDatabase.addUser(user.name, user.email);
    }
    @Override
    public void delete(User user) {
        userDatabase.getUsers().remove(user.id);
    }
    @Override
    public boolean update(User user) {
        boolean ok = false;
        User existingUser = userDatabase.getUsers().get(user.id);
        if (existingUser != null) {
            existingUser.name = user.name;
            existingUser.email = user.email;
            ok = true;
        }
        return ok;
    }
}

Unser echtes UserDao würde zur Laufzeit einen EntityManager statt der InMemoryDatabase injiziert bekommen. Darauf geht das Tutorial über JPA genauer ein. User REST Service lässt sich damit aber schon fertig stellen.


  1. die lokale Variable ok in der update Funktion entspricht unserem Style Guide, der nur ein einziges return Statement pro Funktion unmittelbar vor der schliessenden Klammer fordert.