Sunday, May 4, 2014

dynamic enum

This is to illustrate how to use DynamicEnum to create singleton instances of value objects. Please pay attention to ActorCreator, it is different from AwardCreator.
package algocraft.function.singleton;


import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;

import java.util.Collection;

public class DynamicEnum {

    private static Table<Class, Object, Object> map = HashBasedTable.create();

    public static synchronized <T, K> T getOrCreate(Class<T> tClass, K key, Creator<K, T> creator) {
        T value = (T) map.get(tClass, key);
        if (value == null) {
            value = creator.create(key);
            map.put(tClass, key, value);
        }
        return value;
    }

    public static <T> Collection<T> values(Class<T> tClass) {
        return (Collection<T>) map.row(tClass).values();
    }

    public static int hashCode(Object... objects) {
        int result = objects[0].hashCode();
        for (int i = 1; i < objects.length; i++) {
            result = 31 * result + objects[i].hashCode();
        }
        return result;
    }
}



package algocraft.function.singleton;

import java.util.Collection;
import java.util.List;

import static algocraft.function.singleton.Actor.ActorCreator.ACTOR_CREATOR;
import static com.google.common.collect.Lists.newArrayList;
import static org.apache.commons.lang.builder.ToStringBuilder.reflectionToString;
import static org.apache.commons.lang.builder.ToStringStyle.SHORT_PREFIX_STYLE;

public class Actor {

    public final String firstName;
    public final String lastName;

    private Actor(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public static Actor valueOf(String firstName, String lastName) {
        return DynamicEnum.getOrCreate(Actor.class, new Actor(firstName, lastName), ACTOR_CREATOR);
    }

    @Override
    public String toString() {
        return reflectionToString(this, SHORT_PREFIX_STYLE);
    }

    @Override
    public int hashCode() {
        return DynamicEnum.hashCode(firstName, lastName);
    }

    @Override
    public boolean equals(Object that) {
        return (that instanceof Actor) && equals((Actor) that);
    }

    public boolean equals(Actor that) {
        return this.firstName.equals(that.firstName) && this.lastName.equals(that.lastName);
    }

    public static Collection<Actor> values() {
        return DynamicEnum.values(Actor.class);
    }

    enum ActorCreator implements Creator<Actor>, Actor> {
        ACTOR_CREATOR;

        @Override
        public Actor create(Actor key) {
            return key;
        }

    }
}


package algocraft.function.singleton;


import java.util.Collection;

import static algocraft.function.singleton.Award.AwardCreator.AWARD_CREATOR;
import static algocraft.function.singleton.DynamicEnum.getOrCreate;
import static org.apache.commons.lang.builder.ToStringBuilder.reflectionToString;
import static org.apache.commons.lang.builder.ToStringStyle.SHORT_PREFIX_STYLE;

public class Award {

    public final int level;

    private Award(int level) {
        this.level = level;
    }

    public static Award valueOf(int level) {
        return getOrCreate(Award.class, level, AWARD_CREATOR);
    }

    @Override
    public String toString() {
        return reflectionToString(this, SHORT_PREFIX_STYLE);
    }

    @Override
    public int hashCode() {
        return level;
    }

    @Override
    public boolean equals(Object that) {
        return (that instanceof Award) && equals((Award) that);
    }

    public boolean equals(Award that) {
        return that.level == this.level;
    }

    public static Collection<Award> values() {
        return DynamicEnum.values(Award.class);
    }

    enum AwardCreator implements Creator<Integer, Award> {
        AWARD_CREATOR;

        @Override
        public Award create(Integer key) {
            return new Award(key);
        }
    }
}


package algocraft.function.singleton;

import org.apache.commons.lang.time.StopWatch;
import org.junit.Test;

import java.util.concurrent.*;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class AwardTest {

    public static final int INT = 200;

    @Test
    public void testValueOf() throws Exception {

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        ExecutorService executorService = Executors.newFixedThreadPool(50);

        Future<Award>[] future = new Future[INT];

        final CountDownLatch latch = new CountDownLatch(1);

        for (int i = 0; i < INT; i++) {

             future[i] = executorService.submit(new Callable<Award>() {
                @Override
                public Award call() throws Exception {
                    latch.await();
                    Award.valueOf(1);
                    Award.valueOf(2);
                    Award.valueOf(3);
                    Award.valueOf(4);
                    return Award.valueOf(1);
                }
            });
        }
        latch.countDown();
        Award award = Award.valueOf(1);
        System.out.println(award);
        for (int i = 0; i < INT; i++) {
            assertTrue(award == future[i].get());
            assertFalse(award.equals(future[i]));
            assertTrue(award.equals(future[i].get()));
        }
        System.out.println(Award.values());
    }
}



package algocraft.function.singleton;

import org.apache.commons.lang.time.StopWatch;
import org.junit.Test;

import java.util.concurrent.*;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class ActorTest {

    public static final int INT = 200;

    @Test
    public void testValueOf() throws Exception {

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        ExecutorService executorService = Executors.newFixedThreadPool(50);

        Future<Actor>[] future = new Future[INT];

        final CountDownLatch latch = new CountDownLatch(1);

        for (int i = 0; i < INT; i++) {

            future[i] = executorService.submit(new Callable<Actor>() {
                @Override
                public Actor call() throws Exception {
                    latch.await();
                    Actor.valueOf("Tom", "Cruise");
                    Actor.valueOf("Tom", "Hanks");
                    Actor.valueOf("Sean", "Penn");
                    return Actor.valueOf("Tom", "Cruise");
                }
            });
        }
        latch.countDown();
        Actor actor = Actor.valueOf("Tom", "Cruise");
        System.out.println(actor);
        for (int i = 0; i < INT; i++) {
            assertTrue(actor == future[i].get());
            assertFalse(actor.equals(future[i]));
            assertTrue(actor.equals(future[i].get()));
        }
        System.out.println(Actor.values());

    }

    @Test
    public void whyNeedIt() {
        List<Actor> actorsForMovie1 = newArrayList(
                Actor.valueOf("Tom", "Hanks"),
                Actor.valueOf("Tom", "Cruise"),
                Actor.valueOf("Sean", "Penn")
        );
        List<Actor> actorsForMovie2 = newArrayList(
                Actor.valueOf("Tom", "Hanks"),
                Actor.valueOf("Tom", "Cruise"),
                Actor.valueOf("Sean", "Penn")
        );
        List<Actor> actorsForMovie3 = newArrayList(
                Actor.valueOf("Tom", "Hanks"),
                Actor.valueOf("Tom", "Cruise"),
                Actor.valueOf("Sean", "Penn")
        );

        for (int i = 0; i < actorsForMovie1.size(); i++) {
            assertTrue(actorsForMovie1.get(i) == actorsForMovie2.get(i));
            assertTrue(actorsForMovie2.get(i) == actorsForMovie3.get(i));
        }

    }
}

Why do we need this DynamicEnum? From the about test whyNeedIt, you can see, no matter how many collections the instance is added to, only one copy of the object is created.

No comments:

Post a Comment