Іноді дублювання коду є результатом "каламбура": дві речі виглядають однаково, але ні.
Цілком можливо, що надмірне абстрагування може порушити справжню модульність вашої системи. За режиму модульності ви повинні вирішити, "що, можливо, зміниться?" і "що стабільно?". Те, що є стабільним, вкладається в інтерфейс, а те, що є нестабільним, інкапсулюється у реалізацію модуля. Потім, коли все змінюється, зміна, яку потрібно внести, ізолюється до цього модуля.
Рефакторинг необхідний, коли те, що ви вважали стабільним (наприклад, цей виклик API завжди матиме два аргументи), має змінитися.
Отже, для цих двох дублюваних фрагментів коду я б запитав: чи потрібна зміна, необхідна для одного, означає, що і інша повинна бути змінена?
Як ви відповісте на це питання, ви можете краще зрозуміти, якою може бути хороша абстракція.
Шаблони дизайну також є корисними інструментами. Можливо, ваш дублюваний код виконує обхід певної форми, і слід застосувати шаблон ітератора.
Якщо у вашому дублюваному коді є кілька повернених значень (і тому ви не можете виконати простий метод вилучення), можливо, вам слід зробити клас, який містить значення, що повертаються. Клас може викликати абстрактний метод для кожної точки, який змінюється між двома фрагментами коду. Потім ви зробите дві конкретні реалізації класу: по одному для кожного фрагмента. [Це фактично шаблон дизайну Методу шаблонів, не плутати з поняттям шаблонів у C ++. Крім того, те, що ви дивитесь, може бути краще вирішено за допомогою стратегії.]
Ще один природний і корисний спосіб подумати над цим - це функції вищого порядку. Наприклад, виготовлення лямбда або використання анонімних внутрішніх класів для коду для переходу до абстракції. Як правило, ви можете видалити дублювання, але якщо між ними насправді немає зв’язку [якщо одна зміниться, то й інша], тоді вам може бути шкода модульність, не допомагаючи їй.