HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Singleton interface in Java

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
interfacesingletonjava

Problem

I've created a singleton by means of an interface. I don't want to make a thing with getInstance() etc because I don't care about inheritance, and it's redundant and non-declarative.

Are there downsides of doing it this way? Any redundancy I can remove? Is there a cleaner/more consise way to do this? I like doing it this way because methods are first class because I can pass them to map etc (e.g: map(Dog.sleep, someListOfNumbers)) and it supports currying if you have functions like F>>.

Note the F interface is declared elsewhere, I just inlined it here to have less files.

package a;
import static a.Dog.State.*;
public interface Dog {
    interface F {
        Y f(X x);
    }

    static final class State {
        protected static boolean dead;
        protected static String name = "leo";
        protected static Integer age = 0;
    }

    F getName = new F() {
        public String f(Void _) {
            return name;
        }
    };

    F die = new F() {
        public Void f(Void _) {
            System.out.println("X_X");
            dead = true;
            return null;
        }
    };

    F woof = new F() {
        public Void f(Void _) {
            if (dead)
                throw new IllegalStateException("cannot woof when dead");
                System.out.println("woof");
            return null;
        }
    };

    F sleep = new F() {
        public Void f(Integer years) {
            if (dead)
                throw new IllegalStateException("cannot sleep when dead");
            age += years;
            return null;
        }
    };

    F getAge = new F() {
        public Integer f(Void _) {
            return age;
        }
    };
}


A test:

```
import a.Dog;
class Main {
public static void main(String[] args) {
System.out.println(
"The dog's name is " + Dog.getName.f(null) +
" and is " + Dog.getAge.f(null) + " years old"
);
Dog.sleep.f(2);
System.out.println(
"The d

Solution

Your implementation fails to adhere to some important Object Oriented principles.

-
Encapsulation

The state of your singleton can be modified by others.

package a;

public class Cat implements Dog {

    static {
        State.age = 17;
    }

    public int age() {
        return Cat.getAge.f(null);
    }

    public void setAge(int age) {
        State.age = age;
    }

    public static void main(String[] args) {
        System.out.println(new Cat().age());
    }
}


-
Polymorphism

This is basically a problem that comes with the singleton pattern itself. However, a more conventional approach to implementing a singleton can deal with this issue :

package b;

public interface Dog {

    String getName();

    void die();

    void woof();
}


and the singleton implementation :

package b;

public enum SingleDog implements Dog {
    INSTANCE;

    private String name;
    private boolean dead;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void die() {
        System.out.println("X_X");
        dead = true;
    }

    @Override
    public void woof() {
        if (dead) {
            throw new IllegalStateException("cannot woof when dead");
        }
        System.out.println("woof");
    }
}


This way clients can depend on the interface and not the singleton implementation. i.e. that there can only be one instance is now an implementation detail, and not an unchangeable design decision.

If you want to do functional programming, Java is probably not the language you should be using.

Code Snippets

package a;

public class Cat implements Dog {

    static {
        State.age = 17;
    }

    public int age() {
        return Cat.getAge.f(null);
    }

    public void setAge(int age) {
        State.age = age;
    }

    public static void main(String[] args) {
        System.out.println(new Cat().age());
    }
}
package b;

public interface Dog {

    String getName();

    void die();

    void woof();
}
package b;

public enum SingleDog implements Dog {
    INSTANCE;

    private String name;
    private boolean dead;


    @Override
    public String getName() {
        return name;
    }

    @Override
    public void die() {
        System.out.println("X_X");
        dead = true;
    }

    @Override
    public void woof() {
        if (dead) {
            throw new IllegalStateException("cannot woof when dead");
        }
        System.out.println("woof");
    }
}

Context

StackExchange Code Review Q#26263, answer score: 5

Revisions (0)

No revisions yet.