Re[2]: Дизайн интерфейса
От: Воронков Василий Россия  
Дата: 14.06.09 15:42
Оценка:
Здравствуйте, Буравчик, Вы писали:

Б>CallType — мне не очень понравилось. Проще это все явно в интерфейс включить.


У меня тоже есть сомнения по поводу CallType. Но вербализировать не могу. А есть ли какие-то причины, можете объяснить почему это плохо?

Б>Теперь методы интерфейса стали похожи на обычный вызов функции, что в общем-то логично


В вашем варианте получается, что при реализации IAggregateFunction придется реализовать метод Execute из интерфейса IScalarFunction, который не нужен.
Тогда уж так:

public interface IScalarFunction : IFunction
{
  IFunctionResult Execute(IFunctionParams params);
}

public interface IAggregateFunction : IFunction
{
  IFunctionResult ExecuteOnFinal(IFunctionParams params);
}


Но при этом IFunction все равно пустой получается
Кстати, IFunctionContext нужен не только для передачи значений. В противном случае можно все это вообще упростить до:

object Execute(params object[] args);


Я вообще думал еще и о таком варианте:

public interface IFunction
{
  IFunctionResult Execute(IFunctionParams params);
}

public interface IAggregateFunction : IFunction
{
  CallType CallType { get; set; }
}


По дизайну ИМХО правильно — по большому счет агрегат это скалярная функция, которая имеет особенности в плане выполнения.
Но тут опять появляется этот самый колл-тайп.

Б>А вот, что в этих интерфейсах должно быть — это вопрос, который непосредственно завязан на методы IFunctionContext.

Б>Непонятно, например, зачем отдельно выделять SetResultValue<T>, void SetResultBlob, SetResultNull, SetResultZeroBlob.

SetResultValue<T> — возвращает тип реализующий IConvertible, благодаря генерику даже можем избежать лишней упаковки-распаковки.
SetResultBlob — массив байт, IConvertible не реализует.
SetResultNull — специальное значение, DBNull.
SetResultZeroBlob — тут особый контракт вызова, блоб заполненный нулями указанной длины.

Б>И непонятны отличия ReportError(FunctionError error) и ReportError(int code, string message).


ReportError(FunctionError error) — одна из стандартных ошибок (выбор по энуму)
ReportError(int code, string message) — пользовательская ошибка.

Если есть предложения, как это оформить по-другому, буду рад выслушать.
Re[3]: Дизайн интерфейса
От: Буравчик Россия  
Дата: 14.06.09 17:44
Оценка: 7 (1) +1
Здравствуйте, Воронков Василий, Вы писали:

ВВ>У меня тоже есть сомнения по поводу CallType. Но вербализировать не могу. А есть ли какие-то причины, можете объяснить почему это плохо?


Да не плохо это. Просто при определенном раскладе этот тип становится не нужным.
Т.е. если в итоге будет принято решение об одном интерфейсе IFunction { execute }, то CallType останется.
Если разделить на два интерфейса Scalar и Agregate, и в добавок в Aggregate выделить отдельные методы (step и final), то CallType станет не нужным.

Другое дело, что второй вариант, на мой взгляд более правильный => CallType не нужен.

Б>>Теперь методы интерфейса стали похожи на обычный вызов функции, что в общем-то логично

ВВ>В вашем варианте получается, что при реализации IAggregateFunction придется реализовать метод Execute из интерфейса IScalarFunction, который не нужен.

Да, напутал я. Должно быть как-то так.

public interface IScalarFunction : IFunction
{
  IFunctionResult Execute(IFunctionParams params);
}

public interface IAggregateFunction : IFunction
{
  void ExecuteOnStep(...);
  IFunctionResult ExecuteOnFinal(...);
}


ВВ>Тогда уж так:

ВВ>public interface IAggregateFunction : IFunction
ВВ>{
ВВ> IFunctionResult ExecuteOnFinal(IFunctionParams params);
ВВ>}
ВВ>[/c#]

Как определить в каком режиме (step, final) вызывает aggregate?

ВВ>Но при этом IFunction все равно пустой получается

А нужен ли вообще данный интерфейс? Кто на него завязан будет и что делать будет?

Но даже если останется пустой. Сейчас пустой — потом не пустой. Если это добавляет понятности коду — почему нет?
Более того, если Вам нужно определять является ли данный класс реализацией пользовательской функции, даже пустой интерфейс будет полезен.

ВВ>Кстати, IFunctionContext нужен не только для передачи значений. В противном случае можно все это вообще упростить до:


Я в IFunctionContext увидел:
1. Получение параметров вызова функции
2. Предача результата функции.

п.1 реализауется прямой передачей параметров, п.2. реализуется IFunctionResult

Что еще?

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


В целом согласен. Но в данном случае их равнять нельзя.

Б>>А вот, что в этих интерфейсах должно быть — это вопрос, который непосредственно завязан на методы IFunctionContext.

ВВ>Если есть предложения, как это оформить по-другому, буду рад выслушать.

Например, так

class ValueResult<T>: IFunctionResult

class BlobResult: IFunctionResult
{
  BlobResult(byte[]);
  BlobResult(); // zero blob
}

class NullResult: IFunctionResult

class ErrorResult: IFunctionResult
{
  ErrorResult(FunctionError); // standard error
  ErrorResult(code, msg);     // user error
}


Использование:

// вычисляет сумму чисел, хранящихся как строки
// StringSum("123", "123", "3") = 249
class StringSumFunction: IAggregateFunction
{
    long sum = 0;
    bool error = false;

    void ExecuteOnStep(params) 
    {
      if (error)
        return
       
      if (является_числом (params[str])) then
        sum += parse(params[str])
      else
        error = true;
    }
    
  IFunctionResult ExecuteOnFinal (...)
  {
    if (error)
      return ErrorResult(0, "Не могу прочитать число")
    else
      return ValueResult<long>(sum);
  }
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1218>>
Best regards, Буравчик
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.