Дизайн класса: логика преобразования файлов и дизайн класса - PullRequest
2 голосов
/ 18 сентября 2009

Это довольно простой, но довольно общий вопрос, поэтому я хочу услышать, что думают люди. У меня возникла ситуация, когда мне нужно взять существующий файл MSI, обновить его несколькими стандартными изменениями и выложить новый файл MSI (дублирование старого файла с изменениями).

Я начал писать это с помощью нескольких открытых методов и базового пути ввода для исходного MSI. Дело в том, что для правильной работы необходимо следовать строгому пути вызовов от вызывающей стороны:

var custom = CustomPackage(sourcemsipath);
custom.Duplicate(targetmsipath);
custom.Upgrade();
custom.Save();
custom.WriteSmsXmlFile(targetxmlpath);

Было бы лучше поместить всю логику преобразования как часть конструктора вместо того, чтобы делать их доступными как открытые методы? (чтобы вызывающий абонент не знал, что такое «правильный порядок»):

var custom = CustomPackage(sourcemsipath, targetmsipath); // saves converted msi
custom.WriteSmsXmlFile(targetxmlpath); // saves optional xml for sms

Затем конструктор напрямую продублирует MSI-файл, обновит его и сохранит в целевом местоположении. WriteSmsXmlFile все еще является общедоступным методом, так как он не всегда требуется.

Лично мне не нравится, когда конструктор фактически "делает вещи" - я предпочитаю иметь возможность вызывать публичные методы, но кажется неправильным предполагать, что вызывающая сторона должна знать правильный порядок вызовов?

Альтернативой может быть сначала дублировать файл, а затем передать дублированный файл конструктору - но, кажется, лучше, чтобы класс делал это самостоятельно.

Может быть, я получил все это задом наперед и мне нужно два класса: SourcePackage , TargetPackage и передать SourcePackage в конструктор TargetPackage?

Ответы [ 5 ]

2 голосов
/ 18 сентября 2009

Я бы пошел с вашей первой мыслью: соберите всю логику преобразования в одном месте. Нет причин раскрывать эту последовательность пользователям.

Между прочим, я согласен с вами о том, что нельзя помещать действия в конструктор. Я бы, вероятно, не делал этого в конструкторе, а вместо этого делал бы это в отдельном методе конвертера, но это личный вкус.

1 голос
/ 18 сентября 2009

Вы правы, считая, что конструктор не должен выполнять больше работы, чем простая инициализация объекта.

Мне кажется, что вам нужна функция Convert(targetmsipath), которая оборачивает вызовы в Duplicate, Upgrade и Save, устраняя тем самым необходимость для вызывающего абонента знать правильный порядок операций, в то время как в то же время сохраняя логику в конструкторе.

Вы также можете перегрузить его, чтобы включить параметр targetxmlpath, который, при наличии, также вызывает функцию WriteSmsXmlFile. Таким образом, все связанные операции вызываются из одной и той же функции на стороне вызывающего, и порядок операций всегда правильный.

1 голос
/ 18 сентября 2009

В таких ситуациях я обычно использую следующий дизайн:

var task = new Task(src, dst); // required params goes to constructor
task.Progress = ProgressHandler; // optional params setup
task.Run();
1 голос
/ 18 сентября 2009

Может быть, только я, но мысль о конструкторе, который делает все это, заставляет меня дрожать. Но почему бы не предоставить static метод, который делает все это:

public class CustomPackage 
{

    private CustomPackage(String sourcePath) 
    {
         ...
    }

    public static CustomPackage Create(String sourcePath, String targetPath) 
    {
         var custom = CustomPackage(sourcePath);
         custom.Duplicate(targetPath);
         custom.Upgrade();
         custom.Save();
         return custom;
    }
}

Фактическим преимуществом этого метода является то, что вам не придется выдавать экземпляр CustomPackage, если процесс преобразования действительно не был успешным (за исключением необязательных частей).

Редактировать В C # этот фабричный метод может даже использоваться (с использованием делегатов) как "истинная" фабрика в соответствии с фабричным шаблоном :

public interface ICustomizedPackage 
{
    ...
}

public class CustomPackage: ICustomizedPackage 
{
    ...
}

public class Consumer 
{
    public delegate ICustomizedPackage Factory(String,String);

    private Factory factory;

    public Consumer(Factory factory) 
    {
        this.factory = factory;
    }

    private ICustomizedPackage CreatePackage() 
    {
        return factory.Invoke(..., ...);
    }

    ...
}

и позже:

new Consumer(CustomPackage.Create);
0 голосов
/ 18 сентября 2009

Я думаю, что существуют сервис-ориентированные и объектно-ориентированные способы.

Сервис-ориентированным способом было бы создание серии фильтров, проходящих вдоль неизменного объекта (объекта) передачи данных.

var service1 = new Msi1Service();
var msi1 = service1.ReadFromFile(sourceMsiPath);
var service2 = new MsiCustomService();
var msi2 = service2.Convert(msi1);
service2.WriteToFile(msi2, targetMsiPath);
service2.WriteSmsXmlFile(msi2, targetXmlPath);

Объектно-ориентированные способы могут использовать шаблон декоратора .

var decoratedMsi = new CustomMsiDecorator(new MsiFile(sourceMsiPath));
decoratedMsi.WriteToFile(targetMsiPath);
decoratedMsi.WriteSmsXmlFile(targetXmlPath);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...