Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OO: Does container contain bike or chair?

Tags:

java

oop

A container may contain bikes and chairs, both belonging to a person. I would like to check, if the container contains either bikes or chairs of said person. Is this possible without using instanceof?

public class Container {

    public Map<Person, List<Item>> items = new HashMap<>();

    public void add(Person p, Item item) {
        items.get(p).add(item);
    }

    public boolean containsChair(Person owner) {
        for(Item i : items.get(owner)) {
            if(i instanceof Chair) {
                return true;
            }
        }
        return false;
    }

    public boolean containsBike(Person owner) {
        for(Item i : items.get(owner)) {
            if(i instanceof Bike) {
                return true;
            }
        }
        return false;
    }
}

For the purpose of illustration, Item, Bike, Chair, Person are all simplest class stubs:

public class Person { public String name; }
public abstract class Item {}
public class Bike extends Item { public Wheel[] wheels;}
public class Chair extends Item { public Leg[] legs;}
public class Wheel {}
public class Leg {}

In the runner, a Person should be able to add Chairs and Bikes to its container:

import java.util.ArrayList;

public class Runner {
    public static void main(String[] args) {
        Container c = new Container();
        Person p = new Person();

        // Prevent null pointer exception
        c.items.put(p, new ArrayList<>());

        c.add(p, new Chair());

        // True
        System.out.println(c.containsChair(p));
    }
}
like image 518
TMOTTM Avatar asked Oct 09 '18 05:10

TMOTTM


2 Answers

You could add to class Item an abstract method ItemType getType(). ItemType would be an enum enumerating all possible item types.

public abstract class Item {
    public abstract ItemType getType();
}

public enum ItemType {
    BIKE, CHAIR;
}

Implementation of Chair:

public static class Chair extends Item {
    public Leg[] legs;
    @Override
    public ItemType getType() {
        return ItemType.CHAIR;
    }
}

Then you could define a contains method to search for a the given Person if it has an item with a certain ItemType:

public boolean contains(Person owner, ItemType itemType) {
    return items.get(owner).stream().anyMatch(item ->itemType.equals(item.getType()));
}

Or null-safe regarding the owners items list:

public boolean contains(Person owner, ItemType itemType) {
    return Optional.ofNullable(items.get(owner))
            .map(i -> i.stream().anyMatch(item -> itemType.equals(item.getType())))
            .orElse(false);
}

Usage:

public static void main(String[] args) {
    Container c = new Container();
    Person p = new Person();

    // Prevent null pointer exception
    c.items.put(p, new ArrayList<>());

    c.add(p, new Chair());

    // True
    System.out.println(c.contains(p, ItemType.CHAIR));        
}

EDIT
Following this approach there is no need for instanceof checks. The usage of instanceof can be a hint indicating that the design has some flaws.

like image 185
LuCio Avatar answered Oct 16 '22 15:10

LuCio


You can store Bike and Chair in two different datastructure.

public final class Container {
    private final Map<Person, List<Chair>> chairs = new HashMap<>();
    private final Map<Person, List<Bike>> bikes = new HashMap<>();

    public void add(Person p, Chair chair) {
        chairs.putIfAbsent(p, new ArrayList<Chair>());
        chairs.get(p).add(chair);
    }

    public void add(Person p, Bike bike) {
        bikes.putIfAbsent(p, new ArrayList<Bike>());
        bikes.get(p).add(bike);
    }

    public boolean containsChair(Person owner) {
        return chairs.getOrDefault(owner, Collections.emptyList()).size() > 0;
    }

    public boolean containsBike(Person owner) {
        return bikes.getOrDefault(owner, Collections.emptyList()).size() > 0;
    }
}

Note that I also made your instance fields private to hide the fact that data is stored in a Map and avoid the runner code to have the responsibility to instanciate an ArrayList if not existant. Both the class and its fields are also final to achieve a better immutability. Both encapsulation and immutability are considered good practices when doing OOP.

Usage

public static void main(String[] args) {
    Container c = new Container();
    Person p = new Person();
    c.add(p, new Chair());
    System.out.println(c.containsChair(p)); //true
    System.out.println(c.containsBike(p)); //false
}
like image 44
Spotted Avatar answered Oct 16 '22 17:10

Spotted