ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Interface Segregation Principle (ISP)
    Modeling/DesignPattern 2020. 2. 28. 11:14

    1. Overview

    A client should not be forced to depend upon an interface that they do not use.

    2. Description

    2.1 Intuition

    2.1.1 Interface Pollution

    • Large Interfaces
    • Unrelated Methods
    • Classes have empty method implementations
    • Method implementations throw UnsupportedOperationsException or similar
    • Method implementations return null default/dummy values

    3. Example

    3.1 Violate ISP

    interface PersistenceService<T extends Entity> {
        public void save(T entity);
        public void delete(T entity);
        public T findById(Long id);
        public List<T> findByName(String name);
    }
    class Entity {
        private Long id;
        public Long getId() {
            return id;
        }
        public void setId(Long id) {
            this.id = id;
        }
    }
    class User extends Entity {
        private String name;
        private LocalDateTime lastLogin;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public LocalDateTime getLastLogin() {
            return lastLogin;
        }
        public void setLastLogin(LocalDateTime lastLogin) {
            this.lastLogin = lastLogin;
        }
    }
    class Order extends Entity {
        private LocalDateTime orderPlaceOn;
        private double totalValue;
        public LocalDateTime getOrderPlaceOn() {
            return orderPlaceOn;
        }
        public void setOrderPlaceOn(LocalDateTime orderPlaceOn) {
            this.orderPlaceOn = orderPlaceOn;
        }
        public double getTotalValue() {
            return totalValue;
        }
        public void setTotalValue(double totalValue) {
            this.totalValue = totalValue;
        }
    }
    class UserPersistenceService implements PersistenceService<User> {
        private static final Map<Long, User> USERS = new HashMap<>();
    
        @Override
        public void save(User entity) {
            synchronized (USERS) {
                USERS.put(entity.getId(), entity);
            }
        }
    
        @Override
        public void delete(User entity) {
            synchronized (USERS) {
                USERS.remove(entity.getId());
            }
        }
    
        @Override
        public User findById(Long id) {
            synchronized (USERS) {
                return USERS.get(id);
            }
        }
    
        @Override
        public List<User> findByName(String name) {
            synchronized (USERS) {
                return USERS.values().stream().filter(u -> u.getName().equalsIgnoreCase(name)).collect(Collectors.toList());
            }
        }
    }
    class OrderPersistenceService implements PersistenceService<Order>{
        private static final Map<Long, Order> ORDERS = new HashMap<>();
        @Override
        public void save(Order entity) {
            synchronized (ORDERS) {
                ORDERS.put(entity.getId(), entity);
            }
        }
        @Override
        public void delete(Order entity) {
            synchronized (ORDERS) {
                ORDERS.remove(entity.getId());
            }
        }
        @Override
        public Order findById(Long id) {
            synchronized (ORDERS) {
                return ORDERS.get(id);
            }
        }
        // name doesn't make any sense for an order entity
        @Override
        public List<Order> findByName(String name) {
            throw new UnsupportedOperationException("Find by name is not suppored");
        }
    }

    3.2 Follow ISP

    interface PersistenceService<T extends Entity> {
        public void save(T entity);
        public void delete(T entity);
        public T findById(Long id);
    //    public List<T> findByName(String name);
    }
    class Entity {
        private Long id;
        public Long getId() {
            return id;
        }
        public void setId(Long id) {
            this.id = id;
        }
    }
    class User extends Entity {
        private String name;
        private LocalDateTime lastLogin;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public LocalDateTime getLastLogin() {
            return lastLogin;
        }
        public void setLastLogin(LocalDateTime lastLogin) {
            this.lastLogin = lastLogin;
        }
    }
    class Order extends Entity {
        private LocalDateTime orderPlaceOn;
        private double totalValue;
        public LocalDateTime getOrderPlaceOn() {
            return orderPlaceOn;
        }
        public void setOrderPlaceOn(LocalDateTime orderPlaceOn) {
            this.orderPlaceOn = orderPlaceOn;
        }
        public double getTotalValue() {
            return totalValue;
        }
        public void setTotalValue(double totalValue) {
            this.totalValue = totalValue;
        }
    }
    class UserPersistenceService implements PersistenceService<User> {
        private static final Map<Long, User> USERS = new HashMap<>();
    
        @Override
        public void save(User entity) {
            synchronized (USERS) {
                USERS.put(entity.getId(), entity);
            }
        }
    
        @Override
        public void delete(User entity) {
            synchronized (USERS) {
                USERS.remove(entity.getId());
            }
        }
    
        @Override
        public User findById(Long id) {
            synchronized (USERS) {
                return USERS.get(id);
            }
        }
    
    //    @Override
        public List<User> findByName(String name) {
            synchronized (USERS) {
                return USERS.values().stream().filter(u -> u.getName().equalsIgnoreCase(name)).collect(Collectors.toList());
            }
        }
    }
    class OrderPersistenceService implements PersistenceService<Order>{
        private static final Map<Long, Order> ORDERS = new HashMap<>();
        @Override
        public void save(Order entity) {
            synchronized (ORDERS) {
                ORDERS.put(entity.getId(), entity);
            }
        }
        @Override
        public void delete(Order entity) {
            synchronized (ORDERS) {
                ORDERS.remove(entity.getId());
            }
        }
        @Override
        public Order findById(Long id) {
            synchronized (ORDERS) {
                return ORDERS.get(id);
            }
        }
        // name doesn't make any sense for an order entity
    //    @Override
    //    public List<Order> findByName(String name) {
    //        throw new UnsupportedOperationException("Find by name is not suppored");
    //    }
    }

    one solution is to break your interfaces there is. Second is that if a method in an interface is applicable to only a single class then we can either remove that definition and put that only in the class for which it actually makes sense.

    4. Reference

    https://en.wikipedia.org/wiki/Interface_segregation_principle

    'Modeling > DesignPattern' 카테고리의 다른 글

    Flyweight  (0) 2020.02.28
    Dependency Inversion Principle (DIP)  (0) 2020.02.28
    Liskov Substitution Principle (LSP)  (0) 2020.02.28
    Open-Closed Principle (OCP)  (0) 2020.02.26
    Single Responsibility Principle (SRP)  (0) 2020.02.26

    댓글

Designed by Tistory.