Re: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 31.01.17 14:31
Оценка: 7 (2) +2
Здравствуйте, Sharov, Вы писали:

S>У этого интерфейсам есть разные (пока три) реализации. Причем каждой реализации нужен только один GenerateStoragePathForFile, все остальные честно бросают NotImplementedException. Полный ужас и идиотизм, согласен. Продолбал момент, что называется. И вот сейчас задумался как это дело унифицировать.

Какой смысл в юнификации? Не проще ли выбросить интерфейс и оставить три конкретных класса с соответствующим методом каждый?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[17]: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 02.02.17 13:03
Оценка: 6 (1) +1 :))
Здравствуйте, Sharov, Вы писали:

S>>>Мешается работа с aws и файловой системой, т.е. SRP летит.

S>·>Никуда он не летит. Ты сам волен выбирать удобное тебе Responsibility. UserStorageInfo как юнит для работы с информации о storage вполне single.
S>Что значит не летит? Для aws пути генерируются одним образом, своя спецификация, для фс генерируется другим образом. Мешать все это дело на зачем.
И где тут _responsibility_?

S>

S>Во, так и сделал. Только вместо трех методов один интерфейс с абстрактным классом
S>
  код
S>
S>    public abstract class StorageData
S>    {
S>        #region Properties

S>        public abstract Guid Guid { get; }
S>        public abstract string Filename { get; }

S>        #endregion

S>        #region Methods

S>        public abstract string GenerateRelativeLinkForFile();

S>        #endregion
S>    }

S> public interface IUserStorageInfo<T> where T : StorageData
S>    {
S>        #region Methods

S>        string GenerateDataLinkForFile(T storage);

S>        #endregion
S>    }

S>public class FileSystemStorage<T> : IUserStorageInfo<T>, where T : StorageData
S>    {

S>    //....  
S>   }

S>public class AmazonS3Storage : IUserStorageInfo<StorageData> 
S>{
S>//...
S>}
S>


Вместо четырёх методов и двух типов у тебя тут больше 4 типов, до хрена методов, ещё и генерики. "А хотя бы я и жадничаю, зато от чистого сердца."

S>Пока гибче для меня, для рефакторинга и т.д. Т.е. из мелких частей конструирую что-то более крупное.

"А теперь я с этой палкой во что хочешь превращусь. В теле такая приятная гибкость образовалась…"

S>>>Плюс, если добавиться что-то общее, то можно будет использовать template method базового класса. Т.е. больше возможности для маневра в будущем, легче адаптироваться к изменениям.

S>·>Что "что-то"? Есть какое-то конретное требование? Озвучь. Или просто "маловато будет"?
S>На сегодня требований нет. А как на завтра У меня aws s3 появился когда я пришел в смежный отдел для интеграционного тестирования, мол у меня все готово. И выяснилось, что файлы определенного типа надо складывать в s3. С таким подходом может быть все что угодно.
Вот появятся требования — будешь думать как их лучше всего реализовать. А пока "закладываться на будущее" — чистые фантазии.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Нужен совет (или паттерн).
От: stomsky Россия  
Дата: 31.01.17 14:33
Оценка: 6 (1) +1
Здравствуйте, Sharov, Вы писали:

S>Что скажете, посоветуете? Мне пока вариант с унифицированным объектом больше всего нравится.


Может быть эти параметры, специфические для каждого класса через конструкторы передавать без универсального объекта StorageData?
Вот так:
public interface IUserStorageInfo
{
  string GenerateStoragePathForFile(string fileName);
}
public class StorageByProjectName : IUserStorageInfo
{
  public StorageByProjectName(string projectName,Guid entityGuid)
  {
    ...
  }
  public string GenerateStoragePathForFile(string fileName)
  {
    ...
  }
}
public class StorageByEntityType : IUserStorageInfo
{
  public StorageByProjectName(EntityTypeEnum entityTypeEnum)
  {
    ...
  }
  public string GenerateStoragePathForFile(string fileName)
  {
    ...
  }
}
public class StorageByDatetime : IUserStorageInfo
{
  public StorageByProjectName(Datetime dt)
  {
    ...
  }
  public string GenerateStoragePathForFile(string fileName)
  {
    ...
  }
}
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re[15]: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 01.02.17 22:37
Оценка: 6 (1) +1
Здравствуйте, Sharov, Вы писали:

S>>>У StorageData пока что три наследника -- пути по типу файла, по временным каким-то параметрам, и простой вариант по умолчанию. Как-то так.

S>·>Не люблю наследование.
S>А я люблю -- мощнейшая штука.
Поэтому его надо использовать обдуманно, а не пихать везде куда пихается.

S>В общем и целом нормально. Как-то так оно и было, в лоб, что называется. И если надо быстро и может даже качественно, то сойдет. Но.

S>Мешается работа с aws и файловой системой, т.е. SRP летит.
Никуда он не летит. Ты сам волен выбирать удобное тебе Responsibility. UserStorageInfo как юнит для работы с информации о storage вполне single.

S> Не очень страшно, конечно. Далее, потенциально может быть очень много строк кода, т.е. это такой монолит в себе.

Много строк — раздели, тривиальный рефакторинг:
public class AwsS3UserStorageInfo
{
    string GeneratePathForFile(string fileName,string projectName,Guid entityGuid)
    {
        code-code-code
    }
}

public class FilesystemUserStorageInfo
{
    string GeneratePathForFile(string fileName,string projectName,Guid entityGuid);
    {
        code-code-code
    }

    string GeneratePathForFile(string fileName,EntityTypeEnum entityTypeEnum);
    {
        code-code-code
    }

    string GeneratePathForFile(string fileName,Datetime dt);
    {
        code-code-code
    }

}


S> Я же пока решил метод GenerateFilesystemPathForFile

S>разнести по классам, а параметры сделать параметрами конструктора. Мне этот подход кажется гибче.
Гибче в чём? В каких сценариях использования?

S>Плюс, если добавиться что-то общее, то можно будет использовать template method базового класса. Т.е. больше возможности для маневра в будущем, легче адаптироваться к изменениям.

Что "что-то"? Есть какое-то конретное требование? Озвучь. Или просто "маловато будет"?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 31.01.17 14:54
Оценка: +2
Здравствуйте, Sharov, Вы писали:

S>·>Какой смысл в юнификации? Не проще ли выбросить интерфейс и оставить три конкретных класса с соответствующим методом каждый?

S>Искать(resolve) в контейнере через интерфейс, как вариант. А как от там и кем был сконфигурирован вопрос десятый.
Это плохой вариант, негодный. Не надо интерфейсы создавать лишь потому что "контейнер". Но если это такая политика партии и бороться невозможно — создай три интерфейса, пусть хлебают, за подробностями сюда
Автор: IQuerist
Дата: 27.07.16
.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: Нужен совет (или паттерн).
От: Sinix  
Дата: 31.01.17 16:17
Оценка: +2
Здравствуйте, Sharov, Вы писали:

S>Далее, интерфейс создавался "потому что общая семантика".

Ну, пока получается, что общего не больше, чем между поиском в ФС и поиском контрола на форме. Смысл тут в общем интерфейсе?


S>т.е. сервис запихивает нужную ему реализацию в контейнер, затем эту использует.

А напрямую заиспользовать, без промежуточных запихнул-достал, почему нельзя?


S>А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?

Потому что ваш контракт врёт пользователю: 2/3 комбинаций из IUserStorageInfo и StorageData или бросают NotImplementedException, или требуют заполнения игнорируемых по факту полей. Зашибись юзабилити
Re[7]: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 31.01.17 17:36
Оценка: +2
Здравствуйте, Sharov, Вы писали:

S>·>Так ведь нет кода переиспользуемого. Или в начальном вопросе что-то упущено, было только сказано что три независимые имплементации. То что общая семантика можно выразить по-другому, например, разместив классы в одном и том же namespace/package.

S>Кода нет, потому что плясать для каждой сущности приходится отдельно. А так можно было бы prgoramming against interface.
Так надо с кода начинать, а не с абстрактных всемогуторов, которые непонятно что должны делать конкретно.
Попробуй TDD — опиши необходимые сценарии в виде тестов и начни свой пляс от них.

S>>>А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?

S>·>Потому что KISS.
S>Ну и что же сложного в общем полиморфном объекте, который каждый использует на свое усмотрение?
Так ты попробуй написать оба варианта кода — три независимых класса, которые делают то что надо. А потом своё решение — абстрактный класс, три полиморфных потомка, конструкторы для них. Потом ещё сам сервис, который как-то диспатчит по полиморфному типу (виртуальный метод? визитор? switch?) и потом попробуй представить как это всё потом _можно_ связывать и какие из комбинаций работоспособны, а какие нет.
И сам увидишь, что твоё решение гораздо сложнее, без каких-либо преимуществ.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Нужен совет (или паттерн).
От: Doc Россия http://andrey.moveax.ru
Дата: 01.02.17 11:48
Оценка: 6 (1)
Здравствуйте, Sharov, Вы писали:

S>Что скажете, посоветуете? Мне пока вариант с унифицированным объектом больше всего нравится.


Унифицированный объект ломает SRP т.к. отвечает за все варианты сразу. Это заставит и работающие с ним классы быть универсальными. Плюс без доп танцев с валидацией объект легко можно загнать в некорректное состояние. А валидировать ему надо уметь все состояния.

Идей как по другому 2
1) Сделать фасад, за которым будут скрываться небольшие интерфейсы + реализации для конкретных параметров.
Хорошо если это все будет жить синглтоном на уровне контейнера.

2) Сделать базовый абстрактный класс-маркер для параметров, наследники только с нужными свойствами. Принимать в интерфейсе базовый класс, от в зависимости от реального типа дергать небольшой класс-реализацию (каждый из которых Lazy). По сути можно завести что-то типа Dictionary<Type, Lazy<IPathGenerator>.
Re[3]: Нужен совет (или паттерн).
От: Doc Россия http://andrey.moveax.ru
Дата: 01.02.17 15:35
Оценка: 6 (1)
Здравствуйте, Sharov, Вы писали:

S>Так?


У меня проще вариант был.
Раз эти реализации не вазаимозаменяемые, а работающие паралельно то что-то вроде

public abstract class ParamBase { }

public class ParamType1 : ParamBase  { ... }
public class ParamType2 : ParamBase  { ... }

public class UserStorageInfo : IUserStorageInfo 
{
   private readonly IDictionary _impl;

   public UserStorageInfo () 
   {
      _impl = new ConcurrentDictionary<Type, Lazy<IUserStorageInfoProvider>>();
      _impl.Add(typeof(ParamType1), new Lazy<UserStorageInfoProvider1>);
      _impl.Add(typeof(ParamType2), new Lazy<UserStorageInfoProvider2>);
   }

   public string GenerateStoragePathForFile(ParamBase p) 
   {
      return _impl[p.GetType].Value.GenerateStoragePathForFile(p);
   }
}


А уже каждый UserStorageInfoProviderN приводит пк своему типу.
Re[13]: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 01.02.17 17:34
Оценка: 6 (1)
Здравствуйте, Sharov, Вы писали:

S>·>или в разных классах по разному? Какие именно сценарии _использования_ ты ожидаешь?

S>пока использую так:
Это не _использование_ функционала. Это сам функционал. Использование я ожидаю примерно такое:
public void onButtonClick()
{
  string path = storageInfo.GeneratePath(fileNameInputBox.getText(), new Guid()....);
  ...
  use(path);
}


S>Классы, импл. IUserStorageInfo окончательно формируют путь, добавляя инф-ию к RelativeLink. Для aws s3 это вообще не нужно -- там будет guid+filename писаться в базу. Это все скорее к StorageProvider типа фс.

S>У StorageData пока что три наследника -- пути по типу файла, по временным каким-то параметрам, и простой вариант по умолчанию. Как-то так.
Не люблю наследование.
Я до сих пор не понимаю — чем такой вариант не устраивает-то?
public class UserStorageInfo
{
    string GenerateAwsS3PathForFile(string fileName,string projectName,Guid entityGuid)
    {
        code-code-code
    }

    string GenerateFilesystemPathForFile(string fileName,string projectName,Guid entityGuid);
    {
        code-code-code
    }

    string GenerateFilesystemPathForFile(string fileName,EntityTypeEnum entityTypeEnum);
    {
        code-code-code
    }

    string GenerateFilesystemPathForFile(string fileName,Datetime dt);
    {
        code-code-code
    }

}
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Нужен совет (или паттерн).
От: Sinix  
Дата: 31.01.17 14:38
Оценка: +1
Здравствуйте, Sharov, Вы писали:


S>Такая ситуация.


UPD Хрень написал, не так понял вопрос. Поправлено.
+1 к мнению "·". Зачем тут вообще интерфейс, если общего API нет?

  общий план действий
1. Выбросить из головы текущую реализацию.
2. Сделать API по реальным сценариям использования. Как я понял, тут общей части нет вообще, поэтому можно с чистой совестью делать свою реализацию для каждого из типов.
3. _Если_ реализации по факту переиспользуют логику — рассмотреть возможность вытащить код в базовый тип / хелпер. Сомнительно для примера топикстартера.
4. _Если_ будет несколько вариантов реализации — рассмотреть вариант с базовым типом. Вычёркиваем, как я понял.
5. _Если_ будет несколько вариантов реализации, предоставляемых через public API — в дополнение к базовому типу завести интерфейс. Тож самое, вычёркиваем.
6. Сравнить текущую реализацию с тем, что планируется сделать, набросать план рефакторинга / тесты, выполнять


S>Что скажете, посоветуете?

FDG почитать
Отредактировано 31.01.2017 14:44 Sinix . Предыдущая версия . Еще …
Отредактировано 31.01.2017 14:39 Sinix . Предыдущая версия .
Re[5]: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 31.01.17 15:29
Оценка: +1
Здравствуйте, Sharov, Вы писали:

S>Далее, интерфейс создавался "потому что общая семантика". Т.е. имеется три класса, которые генерят путь для сохранения файла (пусть даже в S3). Разница между классами в том, какой сервис их использует. Одному нужно учитывать время для сохранения, другому тип сохраняемого файла, третьему проект. Классика же, иметь код, который мог бы всеми (пере)использоваться -- т.е. сервис запихивает нужную ему реализацию в контейнер, затем эту использует.

Так ведь нет кода переиспользуемого. Или в начальном вопросе что-то упущено, было только сказано что три независимые имплементации. То что общая семантика можно выразить по-другому, например, разместив классы в одном и том же namespace/package.

S>А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?

Потому что KISS.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 31.01.17 14:04
Оценка:
Здравствуйте, господа.

Такая ситуация. Допустим имеется интерфейс, типа:
public interface IUserStorageInfo
    {
      
        string GenerateStoragePathForFile(string fileName,string projectName,Guid entityGuid);
        
        string GenerateStoragePathForFile(string fileName,EntityTypeEnum entityTypeEnum);

     string GenerateStoragePathForFile(string fileName,Datetime dt);
    }


У этого интерфейсам есть разные (пока три) реализации. Причем каждой реализации нужен только один GenerateStoragePathForFile, все остальные честно бросают NotImplementedException. Полный ужас и идиотизм, согласен. Продолбал момент, что называется. И вот сейчас задумался как это дело унифицировать. Единственное, что приходит на ум -- создать метод типа
    string GenerateStoragePathForFile(string fileName,StorageData sd);


И каждая реализация будет лезть в этот StorageData по потребностям. Вроде вариант нормальный, взывающая сторона заполняет релевантной инф-ей объект StorageData, и каждая реализация берет то, что нужно. Можно наследоваться от класса, и передавать параметры в конструкторе. Проблема в том, что некоторые реализации я пихаю в контейнер(TinyIoc), и все используют этот экземпляр (по сути singleton).

Что скажете, посоветуете? Мне пока вариант с унифицированным объектом больше всего нравится.
Кодом людям нужно помогать!
Re[2]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 31.01.17 14:48
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sharov, Вы писали:


S>>У этого интерфейсам есть разные (пока три) реализации. Причем каждой реализации нужен только один GenerateStoragePathForFile, все остальные честно бросают NotImplementedException. Полный ужас и идиотизм, согласен. Продолбал момент, что называется. И вот сейчас задумался как это дело унифицировать.

·>Какой смысл в юнификации? Не проще ли выбросить интерфейс и оставить три конкретных класса с соответствующим методом каждый?

Искать(resolve) в контейнере через интерфейс, как вариант. А как от там и кем был сконфигурирован вопрос десятый.
Кодом людям нужно помогать!
Re[4]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 31.01.17 15:08
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sharov, Вы писали:


S>>·>Какой смысл в юнификации? Не проще ли выбросить интерфейс и оставить три конкретных класса с соответствующим методом каждый?

S>>Искать(resolve) в контейнере через интерфейс, как вариант. А как от там и кем был сконфигурирован вопрос десятый.

·>Это плохой вариант, негодный. Не надо интерфейсы создавать лишь потому что "контейнер". Но если это такая политика партии и бороться невозможно — создай три интерфейса, пусть хлебают, за подробностями сюда
Автор: IQuerist
Дата: 27.07.16
.


Согласен, читал. TinyIoc маленький, опереточный. Он вообще мною был использован по началу для интеграционных тестов. Потом пошел в обычный код.

Далее, интерфейс создавался "потому что общая семантика". Т.е. имеется три класса, которые генерят путь для сохранения файла (пусть даже в S3). Разница между классами в том, какой сервис их использует. Одному нужно учитывать время для сохранения, другому тип сохраняемого файла, третьему проект. Классика же, иметь код, который мог бы всеми (пере)использоваться -- т.е. сервис запихивает нужную ему реализацию в контейнер, затем эту использует.

А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?
Кодом людям нужно помогать!
Re[6]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 31.01.17 16:38
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Здравствуйте, Sharov, Вы писали:


S>>Далее, интерфейс создавался "потому что общая семантика".

S>Ну, пока получается, что общего не больше, чем между поиском в ФС и поиском контрола на форме. Смысл тут в общем интерфейсе?

Общее -- генерация пути хранения файла в зависимости от используемого хранилища -- фс или aws s3 -- и в зависимости от используемого сервиса. Для каждого сервиса нужно генерировать путь со своими нюансами.

S>>т.е. сервис запихивает нужную ему реализацию в контейнер, затем эту использует.

S>А напрямую заиспользовать, без промежуточных запихнул-достал, почему нельзя?

Можно, по сути так и происходит. Но поскольку четкого тз нет, решил притащить контейнер, чтобы можно было в случае чего протаскивать объекты. Пока решил протаскивать объекты, отвечающие за генерацию пути. Знаю, звучит прикольно.


S>>А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?

S>Потому что ваш контракт врёт пользователю: 2/3 комбинаций из IUserStorageInfo и StorageData или бросают NotImplementedException, или требуют заполнения игнорируемых по факту полей. Зашибись юзабилити

Я в курсе, поэтому эта тема и создана.
Кодом людям нужно помогать!
Re[6]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 31.01.17 16:41
Оценка:
Здравствуйте, ·, Вы писали:


S>>Далее, интерфейс создавался "потому что общая семантика". Т.е. имеется три класса, которые генерят путь для сохранения файла (пусть даже в S3). Разница между классами в том, какой сервис их использует. Одному нужно учитывать время для сохранения, другому тип сохраняемого файла, третьему проект. Классика же, иметь код, который мог бы всеми (пере)использоваться -- т.е. сервис запихивает нужную ему реализацию в контейнер, затем эту использует.


·>Так ведь нет кода переиспользуемого. Или в начальном вопросе что-то упущено, было только сказано что три независимые имплементации. То что общая семантика можно выразить по-другому, например, разместив классы в одном и том же namespace/package.


Кода нет, потому что плясать для каждой сущности приходится отдельно. А так можно было бы prgoramming against interface.

S>>А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?

·>Потому что KISS.

Ну и что же сложного в общем полиморфном объекте, который каждый использует на свое усмотрение?
Кодом людям нужно помогать!
Re[2]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 31.01.17 16:46
Оценка:
Здравствуйте, stomsky, Вы писали:

S>Здравствуйте, Sharov, Вы писали:


S>>Что скажете, посоветуете? Мне пока вариант с унифицированным объектом больше всего нравится.


S>Может быть эти параметры, специфические для каждого класса через конструкторы передавать без универсального объекта StorageData?

S>Вот так:
S>

S>


Да, это был запасной вариант. И судя по реакции публики не самый худший...
Кодом людям нужно помогать!
Re[7]: Нужен совет (или паттерн).
От: Sinix  
Дата: 31.01.17 16:52
Оценка:
Здравствуйте, Sharov, Вы писали:

S>Общее -- генерация пути хранения файла в зависимости от используемого хранилища -- фс или aws s3 -- и в зависимости от используемого сервиса. Для каждого сервиса нужно генерировать путь со своими нюансами.

Ну как и сказал — вы пытаетесь ответственность хранилища вытащить в отдельное API. Кому оно поможет? Пользователям — нет, реализаторам хранилищ — тож нет, коду, который будет выступать как посредник между хранилищами и клиентским API — снова нет. У вас нет по факту общего API. Спрятать N несовместимых перегрузок за одной с классом параметров — это не оно

Если всё-таки хочется абстрагироваться от хранилища любой ценой — см последовательность выше: собираем public API по реальным сценариям, смотрим, чем придётся пожертвовать ради сохранения простоты API, реализуем. Подход "обобщил, потому что внешне похоже" на практике не работает — абстракции начинают протекать систематически.



S>>А напрямую заиспользовать, без промежуточных запихнул-достал, почему нельзя?

S>Можно, по сути так и происходит. Но поскольку четкого тз нет, решил притащить контейнер

А не проще сначала написать код, посмотреть, что по факту дублируется, и только потом заводить общий интерфейс? Может, у вас вообще ничего общего не останется — смысл усложнять код на ровном месте?
Re[8]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 31.01.17 17:47
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sharov, Вы писали:


S>>·>Так ведь нет кода переиспользуемого. Или в начальном вопросе что-то упущено, было только сказано что три независимые имплементации. То что общая семантика можно выразить по-другому, например, разместив классы в одном и том же namespace/package.

S>>Кода нет, потому что плясать для каждой сущности приходится отдельно. А так можно было бы prgoramming against interface.
·>Так надо с кода начинать, а не с абстрактных всемогуторов, которые непонятно что должны делать конкретно.

Код надо продумать, абстрактные всемогуторы помогают начать думать в правильном направлении.

·>Попробуй TDD — опиши необходимые сценарии в виде тестов и начни свой пляс от них.


TDD я не использую.

S>>>>А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?

S>>·>Потому что KISS.
S>>Ну и что же сложного в общем полиморфном объекте, который каждый использует на свое усмотрение?
·>Так ты попробуй написать оба варианта кода — три независимых класса, которые делают то что надо. А потом своё решение — абстрактный класс, три полиморфных потомка, конструкторы для них. Потом ещё сам сервис, который как-то диспатчит по полиморфному типу (виртуальный метод? визитор? switch?) и потом попробуй представить как это всё потом _можно_ связывать и какие из комбинаций работоспособны, а какие нет.
·>И сам увидишь, что твоё решение гораздо сложнее, без каких-либо преимуществ.

Я тут для себя сейчас таблицу составил, где у меня три способа генерации пути (общий, по типу, по дате) и 2 провайдера -- aws s3 и фс. Всего шесть вариантов. Ну если я использую s3, то там на самом деле всего 4 (3 для фс и один s3 для всех остальных). В идеале хотелось бы накатать какой-нибудь обобщенный класс с двумя параметрами-типами -- способ генерации и тип хранилища. Ну либо принимать два соотв. интерфейса. Как-то так.
Кодом людям нужно помогать!
Re[9]: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 31.01.17 20:26
Оценка:
Здравствуйте, Sharov, Вы писали:

S>>>Кода нет, потому что плясать для каждой сущности приходится отдельно. А так можно было бы prgoramming against interface.

S>·>Так надо с кода начинать, а не с абстрактных всемогуторов, которые непонятно что должны делать конкретно.
S>Код надо продумать, абстрактные всемогуторы помогают начать думать в правильном направлении.
Может быть и помогают думать, не знаю, все думают по-разному... но по каким критериям ты определяешь правильность направления?

S>·>Попробуй TDD — опиши необходимые сценарии в виде тестов и начни свой пляс от них.

S>TDD я не использую.
Так начинай.

S>·>Так ты попробуй написать оба варианта кода — три независимых класса, которые делают то что надо. А потом своё решение — абстрактный класс, три полиморфных потомка, конструкторы для них. Потом ещё сам сервис, который как-то диспатчит по полиморфному типу (виртуальный метод? визитор? switch?) и потом попробуй представить как это всё потом _можно_ связывать и какие из комбинаций работоспособны, а какие нет.

S>·>И сам увидишь, что твоё решение гораздо сложнее, без каких-либо преимуществ.
S>Я тут для себя сейчас таблицу составил, где у меня три способа генерации пути (общий, по типу, по дате) и 2 провайдера -- aws s3 и фс. Всего шесть вариантов.
Но эти способы генерации используются независимо, как я понял. Зачем их пихать в один метод|интерфейс?

S>Ну если я использую s3, то там на самом деле всего 4 (3 для фс и один s3 для всех остальных).

Это предложение я не смог распарсить.

S>В идеале хотелось бы накатать какой-нибудь обобщенный класс с двумя параметрами-типами -- способ генерации и тип хранилища. Ну либо принимать два соотв. интерфейса. Как-то так.

Ещё раз — начни с вариантов использования. Какие сценарии-то?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 31.01.17 20:41
Оценка:
Здравствуйте, ·, Вы писали:

S>>Код надо продумать, абстрактные всемогуторы помогают начать думать в правильном направлении.

·>Может быть и помогают думать, не знаю, все думают по-разному... но по каким критериям ты определяешь правильность направления?

Главное начать, а там посмотрим.

S>>TDD я не использую.

·>Так начинай.

Нет, у меня есть интеграционные тесты. Над проектом я работаю один, много возни, чтобы их писать и поддерживать.


S>>Я тут для себя сейчас таблицу составил, где у меня три способа генерации пути (общий, по типу, по дате) и 2 провайдера -- aws s3 и фс. Всего шесть вариантов.

·>Но эти способы генерации используются независимо, как я понял. Зачем их пихать в один метод|интерфейс?

Независимо, но работу выполняют одну и ту же -- генерируют путь к файлу в зависимости от [общий, по типу, по датеъ + тип хранилища -- фс или s3. Т.е. 2 степени свободы.

S>>Ну если я использую s3, то там на самом деле всего 4 (3 для фс и один s3 для всех остальных).

·>Это предложение я не смог распарсить.

Два типа хранилища (см. выше) и три типа генерации пути. Всего шесть. Для aws s3 по сути у всех будет одно и тоже -- имя файла + гуид. Значит всего четыре способа.

S>>В идеале хотелось бы накатать какой-нибудь обобщенный класс с двумя параметрами-типами -- способ генерации и тип хранилища. Ну либо принимать два соотв. интерфейса. Как-то так.

·>Ещё раз — начни с вариантов использования. Какие сценарии-то?

Генерировать путь к файлу в фс в зависимости от даты, генерировать путь к файлу в фс в зависимости от типа файла, генерировать путь к файлу в aws s3 в зависимости от даты и т.д. Все это можно аккуратно убрать за интерфейсы либо ad-hoc полиморфизм.
Кодом людям нужно помогать!
Re[2]: Нужен совет (или паттерн).
От: Doc Россия http://andrey.moveax.ru
Дата: 01.02.17 11:35
Оценка:
Здравствуйте, stomsky, Вы писали:

S>Может быть эти параметры, специфические для каждого класса через конструкторы передавать без универсального объекта StorageData?


Какой-то странный вариант интерфейс получается. Для генерации скажем 10 Storage Path надо будет 10 раз дернуть сначала фабрику и потом уже сам интерфейс.
Мне кажется это те же яйца что и были, только over-engineered.
Re[2]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 01.02.17 12:04
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Идей как по другому 2

Doc>1) Сделать фасад, за которым будут скрываться небольшие интерфейсы + реализации для конкретных параметров.
Doc>Хорошо если это все будет жить синглтоном на уровне контейнера.

Doc>2) Сделать базовый абстрактный класс-маркер для параметров, наследники только с нужными свойствами. Принимать в интерфейсе базовый класс, от в зависимости от реального типа дергать небольшой класс-реализацию (каждый из которых Lazy). По сути можно завести что-то типа Dictionary<Type, Lazy<IPathGenerator>.




  Так?
 public abstract class StorageData
    {
        
    }

    public class TemporalStorageData: StorageData
    {
      //какие-то свои поля

    }

    public interface IUserStorageInfo2<T> where T:StorageData
    {
        #region Properties

        StorageProvider CurrentStorageProvider { get; }

        #endregion

        #region Methods

        string GenerateDataLinkForFile(string filePath, T data);

        #endregion
    }

    public class TestStorage:IUserStorageInfo2<TemporalStorageData>
    {
        public StorageProvider CurrentStorageProvider { get; private set; }
        public string GenerateDataLinkForFile(string filePath, TemporalStorageData data)
        {
            throw new NotImplementedException();
        }
    }
Кодом людям нужно помогать!
Re[3]: Нужен совет (или паттерн).
От: stomsky Россия  
Дата: 01.02.17 13:01
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Мне кажется это те же яйца что и были, только over-engineered.

Ну это зависит от того, как будут создаваться экземпляры IUserStorageInfo. Может выйти и так...
Красота — наивысшая степень целесообразности. (c) И. Ефремов
Re[11]: Нужен совет (или паттерн).
От: · Великобритания  
Дата: 01.02.17 15:27
Оценка:
Здравствуйте, Sharov, Вы писали:

S>>>TDD я не использую.

S>·>Так начинай.
S>Нет, у меня есть интеграционные тесты. Над проектом я работаю один, много возни, чтобы их писать и поддерживать.
Хорошие юнит-тесты как хорошие дети. Вначале ты поддерживаешь их, а потом они тебя.

S>>>Я тут для себя сейчас таблицу составил, где у меня три способа генерации пути (общий, по типу, по дате) и 2 провайдера -- aws s3 и фс. Всего шесть вариантов.

S>·>Но эти способы генерации используются независимо, как я понял. Зачем их пихать в один метод|интерфейс?
S>Независимо, но работу выполняют одну и ту же -- генерируют путь к файлу в зависимости от [общий, по типу, по датеъ + тип хранилища -- фс или s3. Т.е. 2 степени свободы.
то что выполняют одну и ту же работу это слишком расплывчато. В общем случае все методы выполняют одну и ту же работу — изменяют данные.

S>>>Ну если я использую s3, то там на самом деле всего 4 (3 для фс и один s3 для всех остальных).

S>·>Это предложение я не смог распарсить.
S>Два типа хранилища (см. выше) и три типа генерации пути. Всего шесть. Для aws s3 по сути у всех будет одно и тоже -- имя файла + гуид. Значит всего четыре способа.
В классах-пользователях что будет удобнее всего?

generator.Generate(fileName)

или

generator.Generate(fileName, uuid)
...
generator.Generate(fileName, date)


или

generator.Generate(fileName, date, storageType)


или в разных классах по разному? Какие именно сценарии _использования_ ты ожидаешь?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 01.02.17 16:35
Оценка:
Здравствуйте, Doc, Вы писали:

Doc>Здравствуйте, Sharov, Вы писали:


S>>Так?


Doc>У меня проще вариант был.

Doc>Раз эти реализации не вазаимозаменяемые, а работающие паралельно то что-то вроде

Doc>
Doc>public abstract class ParamBase { }

Doc>public class ParamType1 : ParamBase  { ... }
Doc>public class ParamType2 : ParamBase  { ... }

Doc>public class UserStorageInfo : IUserStorageInfo 
Doc>{
Doc>   private readonly IDictionary _impl;

Doc>   public UserStorageInfo () 
Doc>   {
Doc>      _impl = new ConcurrentDictionary<Type, Lazy<IUserStorageInfoProvider>>();
Doc>      _impl.Add(typeof(ParamType1), new Lazy<UserStorageInfoProvider1>);
Doc>      _impl.Add(typeof(ParamType2), new Lazy<UserStorageInfoProvider2>);
Doc>   }

Doc>   public string GenerateStoragePathForFile(ParamBase p) 
Doc>   {
Doc>      return _impl[p.GetType].Value.GenerateStoragePathForFile(p);
Doc>   }
Doc>}

Doc>


Doc>А уже каждый UserStorageInfoProviderN приводит пк своему типу.


Это может быть универсальный, но явно сложней. Я же стараюсь по максимуму использовать такие инструменты как наследование и параметрические типы.
Кодом людям нужно помогать!
Re[12]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 01.02.17 16:46
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sharov, Вы писали:


S>>>>TDD я не использую.

S>>·>Так начинай.
S>>Нет, у меня есть интеграционные тесты. Над проектом я работаю один, много возни, чтобы их писать и поддерживать.
·>Хорошие юнит-тесты как хорошие дети. Вначале ты поддерживаешь их, а потом они тебя.

Не спорю, но не сложилось на этом проекте. В другом, небольшом, есть.

S>>>>Ну если я использую s3, то там на самом деле всего 4 (3 для фс и один s3 для всех остальных).

S>>·>Это предложение я не смог распарсить.
S>>Два типа хранилища (см. выше) и три типа генерации пути. Всего шесть. Для aws s3 по сути у всех будет одно и тоже -- имя файла + гуид. Значит всего четыре способа.
·>В классах-пользователях что будет удобнее всего?

·>
·>generator.Generate(fileName)
·>

·>или

·>
·>generator.Generate(fileName, uuid)
·>...
·>generator.Generate(fileName, date)
·>


·>или


·>
·>generator.Generate(fileName, date, storageType)
·>


·>или в разных классах по разному? Какие именно сценарии _использования_ ты ожидаешь?


пока использую так:
  код
  public interface IUserStorageInfo<T> where T:StorageData
    {
        #region Properties

        StorageProvider CurrentStorageProvider { get; }

        #endregion

        #region Methods

        string GenerateDataLinkForFile(T storage);

        #endregion
    }


    public abstract class StorageData
    {
        #region Properties

        public abstract Guid Guid { get; }
        public abstract string Filename { get; }

        #endregion

        #region Methods

        public abstract string GenerateRelativeLinkForFile();

        #endregion
    }


Классы, импл. IUserStorageInfo окончательно формируют путь, добавляя инф-ию к RelativeLink. Для aws s3 это вообще не нужно -- там будет guid+filename писаться в базу. Это все скорее к StorageProvider типа фс.
У StorageData пока что три наследника -- пути по типу файла, по временным каким-то параметрам, и простой вариант по умолчанию. Как-то так.
Кодом людям нужно помогать!
Re[14]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 01.02.17 17:47
Оценка:
Здравствуйте, ·, Вы писали:

S>>У StorageData пока что три наследника -- пути по типу файла, по временным каким-то параметрам, и простой вариант по умолчанию. Как-то так.

·>Не люблю наследование.

А я люблю -- мощнейшая штука.

·>Я до сих пор не понимаю — чем такой вариант не устраивает-то?

·>
·>public class UserStorageInfo
·>{
·>    string GenerateAwsS3PathForFile(string fileName,string projectName,Guid entityGuid)
·>    {
·>        code-code-code
·>    }

·>    string GenerateFilesystemPathForFile(string fileName,string projectName,Guid entityGuid);
·>    {
·>        code-code-code
·>    }

·>    string GenerateFilesystemPathForFile(string fileName,EntityTypeEnum entityTypeEnum);
·>    {
·>        code-code-code
·>    }

·>    string GenerateFilesystemPathForFile(string fileName,Datetime dt);
·>    {
·>        code-code-code
·>    }

·>}
·>



В общем и целом нормально. Как-то так оно и было, в лоб, что называется. И если надо быстро и может даже качественно, то сойдет. Но.
Мешается работа с aws и файловой системой, т.е. SRP летит. Не очень страшно, конечно. Далее, потенциально может быть очень много строк кода, т.е. это такой монолит в себе. Я же пока решил метод GenerateFilesystemPathForFile
разнести по классам, а параметры сделать параметрами конструктора. Мне этот подход кажется гибче. Плюс, если добавиться что-то общее, то можно будет использовать template method базового класса. Т.е. больше возможности для маневра в будущем, легче адаптироваться к изменениям.
Кодом людям нужно помогать!
Re[5]: Нужен совет (или паттерн).
От: Sinclair Россия https://github.com/evilguest/
Дата: 02.02.17 08:17
Оценка:
Здравствуйте, Sharov, Вы писали:
S>А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?
Если я правильно понял, то есть три реализации и по три потребности. Итого — девять возможных комбинаций. Из них как минимум 6 заведомо неработоспособные.
Всё, что вы делаете — скрываете этот факт от читателя кода, провоцируя его на ошибку, которая вылезет только в рантайме.
Внимание, вопрос: нахрена? У вас слишком низкие затраты на разработку, и вы хотите нагреть атмосферу за счёт бюджета?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 02.02.17 12:14
Оценка:
Здравствуйте, ·, Вы писали:


S>>В общем и целом нормально. Как-то так оно и было, в лоб, что называется. И если надо быстро и может даже качественно, то сойдет. Но.

S>>Мешается работа с aws и файловой системой, т.е. SRP летит.
·>Никуда он не летит. Ты сам волен выбирать удобное тебе Responsibility. UserStorageInfo как юнит для работы с информации о storage вполне single.

Что значит не летит? Для aws пути генерируются одним образом, своя спецификация, для фс генерируется другим образом. Мешать все это дело на зачем.

S>> Не очень страшно, конечно. Далее, потенциально может быть очень много строк кода, т.е. это такой монолит в себе.

·>Много строк — раздели, тривиальный рефакторинг:
·>
·>public class AwsS3UserStorageInfo
·>{
·>    string GeneratePathForFile(string fileName,string projectName,Guid entityGuid)
·>    {
·>        code-code-code
·>    }
·>}

·>public class FilesystemUserStorageInfo
·>{
·>    string GeneratePathForFile(string fileName,string projectName,Guid entityGuid);
·>    {
·>        code-code-code
·>    }

·>    string GeneratePathForFile(string fileName,EntityTypeEnum entityTypeEnum);
·>    {
·>        code-code-code
·>    }

·>    string GeneratePathForFile(string fileName,Datetime dt);
·>    {
·>        code-code-code
·>    }

·>}
·>



Во, так и сделал. Только вместо трех методов один интерфейс с абстрактным классом
  код
    public abstract class StorageData
    {
        #region Properties

        public abstract Guid Guid { get; }
        public abstract string Filename { get; }

        #endregion

        #region Methods

        public abstract string GenerateRelativeLinkForFile();

        #endregion
    }

 public interface IUserStorageInfo<T> where T : StorageData
    {
        #region Methods

        string GenerateDataLinkForFile(T storage);

        #endregion
    }

public class FileSystemStorage<T> : IUserStorageInfo<T>, where T : StorageData
    {

    //....  
   }

public class AmazonS3Storage : IUserStorageInfo<StorageData> 
{
//...
}


Амазону ничего кроме названия файла и гуида пока не требуется.

S>> Я же пока решил метод GenerateFilesystemPathForFile

S>>разнести по классам, а параметры сделать параметрами конструктора. Мне этот подход кажется гибче.
·>Гибче в чём? В каких сценариях использования?

Пока гибче для меня, для рефакторинга и т.д. Т.е. из мелких частей конструирую что-то более крупное.

S>>Плюс, если добавиться что-то общее, то можно будет использовать template method базового класса. Т.е. больше возможности для маневра в будущем, легче адаптироваться к изменениям.

·>Что "что-то"? Есть какое-то конретное требование? Озвучь. Или просто "маловато будет"?

На сегодня требований нет. А как на завтра У меня aws s3 появился когда я пришел в смежный отдел для интеграционного тестирования, мол у меня все готово. И выяснилось, что файлы определенного типа надо складывать в s3. С таким подходом может быть все что угодно.
Кодом людям нужно помогать!
Re[6]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 02.02.17 12:20
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Sharov, Вы писали:

S>>А чего так все ополчились против общего объекта, который бы каждый использовал на свое усмотрение?
S>Если я правильно понял, то есть три реализации и по три потребности. Итого — девять возможных комбинаций. Из них как минимум 6 заведомо неработоспособные.

Я выше посчитал -- 2 реализации по 3 потребности. Из них 3 совпадают пока друг с другом, итого 4.

S>Всё, что вы делаете — скрываете этот факт от читателя кода, провоцируя его на ошибку, которая вылезет только в рантайме.


Согласен, это был неверный подход. Выше я уже привел вариант более осмысленного решения. Читателем этого кода на 99% буду только я.

S>Внимание, вопрос: нахрена? У вас слишком низкие затраты на разработку, и вы хотите нагреть атмосферу за счёт бюджета?


Причем здесь это, как это относится к исходной проблеме?
Кодом людям нужно помогать!
Re[18]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 02.02.17 13:43
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sharov, Вы писали:


S>>>>Мешается работа с aws и файловой системой, т.е. SRP летит.

S>>·>Никуда он не летит. Ты сам волен выбирать удобное тебе Responsibility. UserStorageInfo как юнит для работы с информации о storage вполне single.
S>>Что значит не летит? Для aws пути генерируются одним образом, своя спецификация, для фс генерируется другим образом. Мешать все это дело на зачем.
·>И где тут _responsibility_?

Да, не прав. Поспешил.

S>>

S>>Во, так и сделал. Только вместо трех методов один интерфейс с абстрактным классом
S>>
  код
S>>
S>>    public abstract class StorageData
S>>    {
S>>        #region Properties

S>>        public abstract Guid Guid { get; }
S>>        public abstract string Filename { get; }

S>>        #endregion

S>>        #region Methods

S>>        public abstract string GenerateRelativeLinkForFile();

S>>        #endregion
S>>    }

S>> public interface IUserStorageInfo<T> where T : StorageData
S>>    {
S>>        #region Methods

S>>        string GenerateDataLinkForFile(T storage);

S>>        #endregion
S>>    }

S>>public class FileSystemStorage<T> : IUserStorageInfo<T>, where T : StorageData
S>>    {

S>>    //....  
S>>   }

S>>public class AmazonS3Storage : IUserStorageInfo<StorageData> 
S>>{
S>>//...
S>>}
S>>


·>Вместо четырёх методов и двух типов у тебя тут больше 4 типов, до хрена методов, ещё и генерики. "А хотя бы я и жадничаю, зато от чистого сердца."

Мне Ваш вариант в принципе подходит, и если я совсем сильно закопаюсь в рефакторинге со своей "специализацией", то откачусь (git revert) к нему.


S>>Пока гибче для меня, для рефакторинга и т.д. Т.е. из мелких частей конструирую что-то более крупное.

·>"А теперь я с этой палкой во что хочешь превращусь. В теле такая приятная гибкость образовалась…"

Кодом людям нужно помогать!
Re[7]: Нужен совет (или паттерн).
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.02.17 04:49
Оценка:
Здравствуйте, Sharov, Вы писали:
S>Причем здесь это, как это относится к исходной проблеме?
Очень просто: вы вместо того, чтобы просто писать код, ищете паттерны, увеличивая количество мусорного кода.
Ваш топик выглядит примерно так: "у меня есть код, который выводит строкии форматирует диск. Точнее, строки на экран пишет один модуль, а форматирует диск — другой. Я придумал интерфейс IFormat, у которого две функции FormatDisk(string diskName) и FormatString(string format, [Params] object arg[]). Есть два класса, каждый реализует по одному методу из двух, а второй бросает NotImplementedException. Каждый модуль будет получать реализацию ILogFormat, сконфигурированную снаружи.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Нужен совет (или паттерн).
От: Sharov Россия  
Дата: 03.02.17 09:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Sharov, Вы писали:

S>>Причем здесь это, как это относится к исходной проблеме?
S>Очень просто: вы вместо того, чтобы просто писать код, ищете паттерны, увеличивая количество мусорного кода.
S>Ваш топик выглядит примерно так: "у меня есть код, который выводит строкии форматирует диск. Точнее, строки на экран пишет один модуль, а форматирует диск — другой. Я придумал интерфейс IFormat, у которого две функции FormatDisk(string diskName) и FormatString(string format, [Params] object arg[]). Есть два класса, каждый реализует по одному методу из двух, а второй бросает NotImplementedException. Каждый модуль будет получать реализацию ILogFormat, сконфигурированную снаружи.

У меня был интерфейс, реализуя который некоторый классы использовали только одни метод, а остальные бросали. Точка предложил вполне вменяемый подход -- http://rsdn.org/forum/design/6685137.1
Автор: ·
Дата: 02.02.17
]здесь[/url], которым можно и воспользоваться. Исходная проблема состояла в том, что ни о какой похожести разных ф-ий речи не было -- генерировать путь к файлу на основе разных данных об этом файле (см. ссылку выше). Я пока каждый метод из предложенного выше решения убрал за соотв. класс, а в конструктор передаю параметры метода. Это безусловно выглядит сложнее, но мне кажется и удобнее (выше я уже описал почему).
Кодом людям нужно помогать!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.