Шаблоны проектирования и приёмы рефакторинга
Следовать принципу разделения интерфейса помогают такие шаблоны проектирования как Адаптер, а также приёмы выделения интерфейса и множественного наследования.
Адаптер
Адаптер — шаблон проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе.
С точки зрения ISP этот шаблон помогает держать интерфейсы чистыми и понятными, а при необходимости совместить несовместимые модули через специальную прослойку (адаптер).
Приложение Курсовик показывает курс доллара к рублю. Оно берёт данные курсов с сайта Центрального банка России. Центробанк отдаёт их в формате XML, а Курсовик работает с JSON.
Адаптер помогает «подружить» модули, которые работают с XML, и модули, которые работают с JSON.
const ApiClient = {
  async getXml(url: string): Promise<XmlString> {
    const response = await fetch(url)
    const data = await response.text()
    return data
  }
}
const xmlJsonAdapter = (xml: XmlString): JsonString => {
  // Конвертируем xml в json:
  return json
}
const parseCourse = (data: JsonString): CourseDict => {
  // ...
  return course
}
(async () => {
  const data = await ApiClient.getXml('api_endpoint')
  const course = parseCourse(xmlJsonAdapter(data))
})()
Также адаптер помогает справляться со «сломанным» API от бекенда и преобразованием одних структур данных в другие.
Из минусов можно назвать:
- добавление ещё одной абстракции в кодовую базу проекта;
 - при создании нового адаптера нужно найти все места, где требуется его использовать.
 
Выделение интерфейса
Выделение интерфейса — это приём, при котором одинаковые методы и поля выносят в отдельный интерфейс.
В качестве примера можно вернуться к Койну из прошлого раздела. Интерфейс Record — это выделенный общий интерфейс, который включает в себя общие для траты и дохода поля.
Выделение интерфейса тесно связано не только с ISP, но и с LSP. Например, оно используется при поиске корня композиции и как вспомогательный инструмент для выделения суперкласса.
Множественное наследование
Множественное наследование используется, например, чтобы реализовать функциональность нескольких интерфейсов:
class Horse implements Animal, Transport {/*...*/}
В TypeScript такое наследование реализуется через миксины.
До этих пор в книге для простоты повествования мы пропускали специальную функцию applyMixins, которая копирует функциональность из родительских классов:
function applyMixins(derivedCtor: any, baseCtors: any[]) {
  baseCtors.forEach(baseCtor => {
    Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
      Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name))
    })
  })
}
Чтобы пример с классом Horse выше сработал, нам необходимо использовать applyMixins следующим образом:
applyMixins(Horse, [Animal, Transport])
Тогда множественное наследование будет работать, как мы ожидаем.