@alekseysinos
Junior data scientist

Что предпочтительнее: наследование или обёртка?

Добрый день.
У меня есть класс который реализует парсер для определенного формата файла.
Я хочу добавить возможность парсинга еще одного формата. Мне представляется, что это можно сделать двумя способами:
1. Реализовать несколько методов парсера в одном классе, проверять расширение файла и вызывать соответствующий метод для обработки:
class FileParser:
    def __init__(self, file_name):
        self.ext = os.path.splitext(file_name)[1]
        if self.ext == '.foo':
            self.data = self._parse_foo(file_name)
        elif self.ext == '.bar':
            self.data = self._parse_bar(file_name)
        else:
            raise TypeError('Wrong file extension')
    def _parse_foo(self,file_name):
        pass
    def _parse_bar(self,file_name):
        pass
    def do_great_things_with_data(self):
        pass

2. Сделать наследование от исходного класса и реализовать в каждом наследнике свою функцию парсинга. При этом сделать фабрику объектов, которая будет создавать наследника нужного класса в соответствие с расширением файла:
class FileParser:
    @staticmethod
    def load_file(cls, file_name):
        ext = os.path.splitext(file_name)[1]
        if ext == '.foo':
            return FooFileParser(file_name)
        elif ext == '.bar':
            return BarFileParser(file_name)
        else:
            raise TypeError('Wrong file extension')
     def do_great_things_with_data(self):
        pass

class FooFileParser(FileParser):
    def __init__(self):
        pass

class BarFileParser(FileParser):
    def __init__(self):
        pass


Какой подход лучше использовать? Какие у каждого есть достоинства/недостатки?
  • Вопрос задан
  • 981 просмотр
Решения вопроса 1
@ivorobioff
Software Engineer
Здесь паттерн стратегия подходит больше. У тебя есть основной класс который занимается парсингом. Он содержит какой-то общий код. Есть другие классы, для каждого формата отдельный класс, которые выполняют непосредственно парсинг и отдают основному классу данные в определенном формате чтобы тот класс понимал.
Такой подход даст вам больше гибкости при добавление новых парсеров, а именно вам не прийдется каждый раз при добавление нового парсера или при изменения одного из парсеров трогать основной класс, что может быть чревато багами. Все парсеры буду полностью независимы друг от друга, при изменения одного парсера других это не затронет.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
SergeyEgorov
@SergeyEgorov
Веб разработчик
Парсер в данном случае просто интерфейс. Поскольку Python - язык с динамической типизацией, то наследование или обертки не требуются вовсе. Просто все конкретные парсеры должны реализовать подразумеваемый интерфейс:
class CsvParser:
    def parse(self, file_name):

class XlsParser:
    def parse(self, file_name);


Экземпляры конкретных парсеров, по мере их появления складываем в обычный словарь, где ключ - это расширение файла, под которое заточен парсер.

{ 'csv': CsvParser(), 'xls': XlsParser(), 'xml': XmlParser() }


В конечном итоге можно реализовать поставщик парсеров, который будет выбирать подходящий парсер из вышеозначенного словаря, используя расширение имени файла:

class ParserProvider:
        def provide(file_name):
Ответ написан
BacCM
@BacCM
C++ почти с рождения
Есть хорошее правило.
Если B является A то это наследование
иначе агрегация

Банально на школьных примерах, стол является предметом мебели, но в него входят столешница, ножки и т.д

А что касается твоего примера. Я бы предпочел вариант на базе 2 но с фабричным/реестровым получением парсера по типу файла.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы