Функциональный стиль C # API дизайн (возвращающий параметр функции, дополненный результатом вычисления) - PullRequest
1 голос
/ 06 февраля 2009

Возникает вопрос об использовании методов функционального программирования в коде C #. Пример

Пусть у нас есть интерфейс

interface IGraph { /*contains vertices and edges*/}

Предположим, нам нужно расположить вершины графа (назначить Point каждой вершине).

interface ILayoutInfo {
  Point GetVertexPoint(vertex);
}

Простой маршрут маршрута может иметь такую ​​подпись:

ILayoutInfo SimpleLayout(IGraph graph);

Что можно использовать таким образом

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutInfo layout = SimpleLayout(graph);
  PrintCoordinates(graph,layout);
}

В дизайне ниже PrintCoordinates нужны как ссылки на график, так и макет.

Рассмотрим дизайн функционального стиля, в котором маршрутизация макетов дополняет графическую информацию с информацией о координатах вершин графа.

ILayoutedGraph SimpleLayoutNew(IGraph graph);

Где ILayoutedGraph реализует ОБА IGraph и ILayoutInfo

void DemoNew() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutedGraph layoutedGraph = SimpleLayoutNew(graph);
  PrintCoordinatesNew(layoutedGraph);
}

1) В этом проекте PrintCoordinatesNew получает только ОДИН параметр. 2) Странный интерфейс ILayoutedGraph родился, который не содержит никаких методов и просто оборачивает другие интерфейсы. Если в какой-то библиотеке есть другие типы, такие как INetwork, то у нас получится создание интерфейсов переноса ILayoutedNetwork, ILayoutedTree (что плохо).

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

Большое спасибо,

PS: более подробный печатный пример можно найти здесь http://tivadj.blogspot.com/2009/02/designing-c-api-in-functional-style.html

Ответы [ 3 ]

0 голосов
/ 07 февраля 2009

Нет проблем с вашим исходным API

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  ILayoutInfo layout = SimpleLayout(graph);
  PrintCoordinates(graph,layout);
}

Это совершенно функционально. Обязательный API будет включать в себя изменение graph после его создания. Например,

void Demo() {
  IGraph graph = CreateGraphInAnyWay();
  graph.Layout(new SimpleLayout()); // changes the state of graph
  PrintCoordinates(graph);
}

Что касается проблемы ILayoutedGraph, ILayoutedTree, ILayoutedQueue и т. Д., Я думаю, вы могли бы обойти это с помощью универсальных классов в функциональных языках и множественного наследования в языках OO, где это разрешено.

Лично я бы порекомендовал дженерики: ILayout<a>, где a - это нечто с ребрами, которые необходимо выложить.

0 голосов
/ 07 февраля 2009

Если вам кажется, что при передаче двух аргументов вас беспокоит то, что ваш макет (ILayoutInfo) связан с графом, используемым для его генерации. Мне было бы бессмысленно передавать макет с графиком, который не использовался для его генерации.

В этом случае вы можете сохранить ссылку на график в информации макета и предоставить аксессор в интерфейсе ILayoutInfo.

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

Если у вас есть другой тип объекта, который может генерировать информацию о макете, используйте общий интерфейс для них или сделайте универсальный ILayoutInfo.

0 голосов
/ 06 февраля 2009

Можете ли вы реализовать это с помощью класса, который реализует оба интерфейса и приведение возвращаемых значений к этому классу?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...