Ответ 1
Реальность такова, что такие преобразования, как правило, не были бы хорошим объектно-ориентированным кодом. Зачем? Поскольку объектно-ориентированный код не просто перемещает функции в методы и данные в члены.
Вместо этого хороший объект должен отвечать за все свои данные и принимать только параметры метода, в которых эти параметры определяют данные, которые будут использоваться.
Это означает, что сопоставление 1:1 от процедурных функций и структур процедурных данных к объектно-ориентированным.
Осмотревшись, я не нашел примеров, которые мне нравились в Интернете, поэтому я просто дам свои правила рефакторинга для преобразования процедурного кода в ООП.
Первый шаг - просто упаковать каждый модуль в качестве объекта. Другими словами, просто создайте объект, содержащий данные и функции. Это ужасно для пуриста, но вы должны что-то начать. Например, если у вас есть модуль BankAccount, теперь у вас будет объект BankAccount.
Очевидно, что функции имели данные, переданные в них из внешних вызовов. Здесь вы ищете, как интернализировать эти данные и сделать их как можно более частными. Цель должна заключаться в том, что вы получаете свои данные в своем конструкторе (по крайней мере, в начальной точке) и удаляете параметры, которые использовались для получения данных вручную, и заменяете их ссылками на эти теперь личные данные. Используя объект BankAccount, все доступ к учетной записи теперь осуществляется с помощью методов объекта, и данные фактической учетной записи были интернализованы.
Многие из ваших функций, вероятно, вернули измененные версии структур данных: прекратите прямое возвращение этих данных и сохраните эти изменения внутри частных структур. Создайте свойства доступа, которые при необходимости возвращают ваши личные данные и отмечают их "устаревшими" (ваша цель - сделать объект хозяином своих данных и возвращать результаты, а не внутренние данные). С объектом BankAccount мы больше не возвращаем фактические данные учетной записи, но у нас есть свойства для CurrentBalance и методы, такие как AverageBalance (int days), чтобы увидеть учетную запись.
В конце концов у вас будет набор самодостаточных объектов, которые по-прежнему мало похожи на то, что вы бы сделали, если бы вы начали с объектов в вашем дизайне, но по крайней мере вы можете продолжить рефакторинг с новыми объектами. Мой следующий шаг обычно обнаруживает, что объекты, созданные из такого рефакторинга, имеют много обязанностей. На данный момент, вероятно, были обнаружены некоторые общие потоки, и вы должны создавать объекты для реорганизации этих общих идей. Если у нас есть BankAccount, у нас, вероятно, есть другие типы учетных записей, и если мы выровняем методы всех этих типов учетных записей, мы можем сделать Учетную запись как базовый класс, который реализует все общие функции, а BackAccount, SavingsAccount и другие реализуют детали.
Как только структура классов начнет складываться, пришло время почувствовать себя лучше в отношении преобразования. Рефакторинг - это процесс, а не конечная точка, поэтому я обычно обнаруживаю, что моя структура классов продолжает развиваться. Одна из приятных вещей о том, что вы получили это, заключается в том, что ваши данные являются частными и управляются с помощью методов, поэтому вы можете реорганизовать внутренности все более и более свободно по мере продвижения.
Одна вещь, которая делает это правдоподобным, - это иметь хорошие модульные тесты. Когда вы делаете процедурные преобразования ООП, я часто поддерживаю старый код как "базовый", поэтому я могу проверить его. I.e., тест может проверять результаты старой процедурной системы. Если вы не соответствуете, это хорошая идея, чтобы выяснить, почему. Я нахожу, что часто появляется ошибка... но иногда ваш новый код очистки действительно делает что-то правильно, что было неправильно в прошлом.
Относительно создания "слишком глубоких" деревьев объектов: это может быть результатом слишком навязчивости в отношении наследования. Я считаю, что композиты часто являются лучшей идеей, где вы реализовали интерфейсы для нескольких объектов, а не пытались получить все эти функции в один родительский объект. Если вы обнаруживаете, что создаете родительские объекты, которые просто представляют собой сочетания наборов функций, рассмотрите упрощение путем создания интерфейса для каждого набора функций и реализации этих интерфейсов.