В идеальном мире
В идеальном мире каждый класс в коде решает одну и только одну задачу, а все задачи структурированы и разделены. Модули в этом случае дополняют друг друга, а их совокупность детально описывает систему.
Допустим, у нас есть задача создать отчёт об активности пользователей и вывести его в нескольких вариантах: как строку HTML или TXT.
Отчёт
Мы создадим класс ReportExporter, который будет заниматься только экспортом данных. Определять необходимый формат будет класс FormatSelector. А форматированием данных будут заниматься классы: HtmlFormatter и TxtFormatter.
// Тип данных для отчёта:
type ReportData = {
  content: string,
  date: Date,
  size: number,
}
// Возможные форматы:
enum ReportTypes {
  Html,
  Txt,
}
// Класс, который занимается экспортом данных:
class ReportExporter {
  name: string
  data: ReportData
  constructor(name: string, data: ReportData) {
    this.name = name
    this.data = data
  }
  export(reportType: ReportTypes): string {
    const formatter: Formatter = FormatSelector.selectFor(reportType)
    return formatter.format(this.data)
  }
}
Форматы экспорта
В соответствии с SRP форматирование данных — это отдельная задача. Поэтому для преобразования данных отчёта в необходимый формат мы создадим отдельные классы.
interface Formatter {
  format(data: ReportData): string
}
// Класс для форматирования в HTML:
class HtmlFormatter implements Formatter {
  format(data: ReportData): string {
    // ...Форматируем данные в HTML и возвращаем:
    return 'html string'
  }
}
// Класс для форматирования в TXT:
class TxtFormatter implements Formatter {
  format(data: ReportData): string {
    // ...Форматируем данные в TXT и возвращаем:
    return 'txt string'
  }
}
Выбор формата
Принцип единственной ответственности подсказывает, что выбор формата не входит ни в задачу форматирования данных, ни в задачу их подготовки. Поэтому существующие классы нам не подойдут.
Для решения этой задачи воспользуемся шаблоном проектирования «Стратегия», который поможет выбрать подходящий формат. (Более подробно «Стратегию» мы разберём в разделе о принципе открытости и закрытости.)
Создадим новый класс FormatSelector, который будет выбирать тип форматирования, в зависимости от настроек.
class FormatSelector {
  private static formatters = {
    [ReportTypes.Html]: HtmlFormatter,
    [ReportTypes.Txt]: TxtFormatter,
  }
  static selectFor(reportType: ReportTypes) {
    const FormatterFactory = FormatSelector.formatters[reportType]
    return new FormatterFactory();
  }
}
const dynamicFormatter = FormatSelector.selectFor(ReportTypes.Html)
dynamicFormatter.format(/*...*/)
Таким образом SRP помогает разделить ответственность за различные задачи между сущностями и сделать это так, чтобы каждая сущность занималась одной задачей.