Принцип открыт-закрыт с конструктором

Изучение принципа «SOLID». Мне интересно, можно ли изменить конструктор, если мне нужно добавить еще какое-то расширение к классу, например. бизнес-логика.

Из того, что я узнал, похоже, что при изменении конструктора я нарушаю принцип «открыто-закрыто», но что, если мне нужно ввести другой класс для выполнения некоторой логики? Как я могу сделать это без модификации конструктора, и вообще, нарушает ли модификация конструктора принцип «открыто-закрыто»?

Рассмотрим пример.

Есть интерфейс

public interface ShopFactory {
    List<Discount> getDiscounts();
    List<Sale> getSales();
}

И есть одна реализация (и, возможно, могут быть другие, если кто-то захочет добавить мою библиотеку в качестве зависимости)

public class CountableDefaultShopFactory implements ShopFactory {

    Counter discountsCounter;
    Counter salesCounter;

    public DefaultShopFactory(Counter discountsCounter, Counter salesCounter) {
        this.discountsCounter = discountsCounter;
        this.salesCounter = salesCounter;
    }

    @Override
    List<Discount> getDiscounts() {
        discountsCounter.count();
        return Discount.defaultDiscounts();
    }

    @Override
    List<Sale> getSales() {
        salesCounter.count();
        return Sale.defaultSales();
    }

}

Так что это довольно просто. CountableDefaultShopFactory реализует ShopFactory, переопределяет два метода и зависит от некоторых Counter объектов, чтобы подсчитать, сколько раз был вызван каждый метод. Каждый метод возвращает некоторые данные с использованием статического метода.

Теперь предположим, что меня попросили добавить еще один метод, и на этот раз мне нужно получить данные из некоторого хранилища, и есть служба, которая предоставляет некоторые данные из этого хранилища. В этом случае мне нужно внедрить эту службу в свой класс, чтобы выполнить операцию.

И это будет выглядеть так:

public class CountableDefaultShopFactory implements ShopFactory {

    Counter discountsCounter;
    Counter salesCounter;
    Counter couponsCounter;
    CouponDAO couponDAO;

    public DefaultShopFactory(Counter discountsCounter, Counter salesCounter, Counter couponsCounter, CouponDAO couponDAO) {
        this.discountsCounter = discountsCounter;
        this.salesCounter = salesCounter;
        this.couponsCounter = couponsCounter;
        this.couponDAO = couponDAO;
    }

    @Override
    List<Discount> getDiscounts() {
        discountsCounter.count();
        return Discount.defaultDiscounts();
    }

    @Override
    List<Sale> getSales() {
        salesCounter.count();
        return Sale.defaultSales();
    }

    @Override
    List<Coupon> getCoupons() {
        couponsCounter.count();
        return couponDAO.getDefaultCoupons();
    }

}

Поэтому мне пришлось изменить свой конструктор, добавив еще один класс Counter и
CouponDAO. Я согласен, что мне нужно добавить еще один Counter под названием couponsCounter, поскольку это счетная реализация ShopFactory. Но добавление CouponDAO мне не подходит.

Мне интересно, есть ли лучшее решение, как это сделать?


person user3127896    schedule 30.12.2016    source источник
comment
Что делает эти Factorys?   -  person weston    schedule 31.12.2016


Ответы (1)


Да, это нарушает Open Closed, но также и SRP, поскольку вы дали классу более чем одну причину для изменения.

Появилось новое требование, и вы могли расширить код без изменения того, что там было, и добавления всех тех новых зависимостей, которые использовались только одним методом. (Если не возражаете, я опущу постфикс Factory):

public interface CouponShop extends Shop {
    List<Coupon> getCoupons();
}

public class CountableCouponShop implements CouponShop {

    public CountableCouponShop(Shop shop, Counter couponsCounter, CouponDAO couponDAO) {
        //assign to fields
    }

    @Override
    List<Discount> getDiscounts() {
        return shop.getDiscounts(); //just delegate to the old implementation of shop
    }

    @Override
    List<Sale> getSales() {
        return shop.getSales();
    }

    @Override
    List<Coupon> getCoupons() {
        couponsCounter.count();
        return couponDAO.getDefaultCoupons();
    }
}
person weston    schedule 31.12.2016
comment
Спасибо за ваш ответ. Не могли бы вы объяснить, почему это нестабильный SRP? - person user3127896; 31.12.2016
comment
Как я уже сказал, вы дали классу более чем одну причину изменить - person weston; 31.12.2016
comment
Я вижу только одну причину, а именно добавление еще одного метода, какие еще? - person user3127896; 31.12.2016
comment
Нет, я имею в виду, что если в нем есть как старый, так и новый код, то есть две причины для изменения. Различные зависимости этих методов подчеркивают, насколько мало у них общего. Это также известно как низкая когезия. - person weston; 31.12.2016