Шаблон стратегии определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми. Стратегия позволяет алгоритму отличаться от клиента, который его использует.
Мы начнем с работы над примером без шаблона разработки стратегии.
Допустим, мы работаем над банковским приложением. В нашем банковском приложении есть класс «Клиент». В нашем классе Customer у нас есть три метода, которые возвращают номер сберегательного счета, один тип возврата кредита и одну процентную ставку возврата кредита.
class Customer{ String getSavingAccountNumber{} String getLoanType(){} double getLoanInterestRate(){}}
Теперь, если мы хотим иметь клиента, у которого есть жилищный кредит. Мы можем расширить класс Customer
class HomeLoanCustomer extend Customer{ String getLoanType(){ return "This a home loan" } double getLoanInterestRate(){ return 8.16 } }
Таким же образом мы можем создать CarLoanCustomer. Но как насчет клиента, у которого нет ссуды, например NoLoanCustomer или RichCustomer, но у него все еще есть getLoanType, getLoanInterestRate выставленный ему метод. Вы можете возразить, что я могу переопределить эти методы, которые ничего не возвращают. Но вопрос в том, во сколько? Каждый раз, когда банк решает добавить новый тип клиента, мы должны переопределить эти методы, убедившись, что мы не возвращаем процентную ставку по ссуде и тип ссуды клиентам, которым эти методы вообще не требуются.
Теперь одно из возможных решений - использовать интерфейс. Нравится
interface LoanCustomer{ String getSavingAccountNumber String getLoanType() } class Customer{ String getSavingAccountNumber{} } class HomeLoanCustomer extend Customer implements LoanCustomer { String getLoanType(){ return "This a home loan" } double getLoanInterestRate(){ return 8.16 } class CarLoanCustomer extend Customer implements LoanCustomer { String getLoanType(){ return "This a car loan" } void double getLoanInterestRate(){ return 11.89 } class RichCustomer extends Customer{}
Как видите, RichCustomer не требует реализации LoanCustomer.
Но вот загвоздка. А как насчет повторного использования кода? Предположим, что если вы хотите что-то изменить в интерфейсе LoanCustomer, вам нужно изменить все клиенты ссуды, которые это реализуют. Итак, мы пришли к выводу, что наш код не работает, когда дело доходит до изменений. Потому что программное обеспечение обязательно изменится в будущем.
Теперь мы воспользуемся шаблоном стратегии стратегии для решения проблемы.
Теперь это наш базовый класс
class Customer{ String getSavingAccountNumber{} String getLoanType(){} double getLoanInterestRate(){}}
Здесь мы должны выяснить, какой алгоритм меняется, а какой остается. В классе клиентов алгоритм getSavingAccountNumber остается неизменным для всех клиентов, потому что, согласно правилам банка, каждый клиент должен иметь сберегательный счет, чтобы пользоваться услугами банка. А алгоритм getLoanType, getLoanInterestRate варьируется от клиента к клиенту.
Итак, в соответствии с шаблоном стратегии нам необходимо инкапсулировать изменяющийся алгоритм. Итак, мы инкапсулируем их в интерфейс или класс и реализуем его семейство алгоритмов.
interface Loanable{ String getLoanType() double getLoanInterestRate() } class HomeLoan implements Loanable{ String getLoanType() // home loan double getLoanInterestRate() //8.9 } class CarLoan implements Loanable{ String getLoanType() // car loan double getLoanInterestRate() //12.95 } class NoLoan implements Loanable{ String getLoanType() // no loan double getLoanInterestRate() //0.0 }
Теперь мы будем использовать их в клиенте, то есть в клиенте и его различных подклассах.
class Customer{ Loanable loanable; String getSavingAccountNumber{} String loanType(){loanable.getLoanType()} double loanInterestRate(){loanable.getLoanInterestRate()} } class HomeLoanCustomer extends Customer{ public HomeLoanCustomer(){ loanable=new HomeLoan() } } class NoLoanCustomer extends Customer{ public HomeLoanCustomer(){ loanable=new NoLoan() } } class RichCustomer extends Customer{ public HomeLoanCustomer(){ loanable=new NoLoan() } }
Теперь, если вы хотите добавить новую функциональность для клиентов, у которых нет ссуды, скажем, банк хочет показать промо, которое гласит: «Получите ссуду с нулевой комиссией за обработку». Мы можем изменить наш класс NoLoan, не затрагивая клиентов. Нравится:
class NoLoan implements Loanable{ String getLoanType() // no loan double getLoanInterestRate() //0.0 void showPromo() //get loan with zero processing fee }
А наш класс HomeLoan вроде этого:
class HomeLoan implements Loanable{ String getLoanType() // home loan double getLoanInterestRate() //5.5 (Home loan now get cheaper) }
Таким образом, наш код готов к внесению изменений, и его можно будет сопровождать. Если банк захочет, в будущем мы сможем добавить новый вид кредита.
Здесь мы использовали композицию вместо наследования с инкапсуляцией алгоритма, который меняется, чтобы подготовить наш код к будущему.
Шаблон стратегии = инкапсуляция различных алгоритмов + композиция