Так как функции являются манипулируемыми и сложными в F #, шаблон стратегии вездесущ.
Например, предположим, что у меня есть определение типа для точки и траектории элементов:
type Point = { X:float; Y:float }
type PathOp =
| Close
| MoveTo of Point
| LineTo of Point
| CurveTo of Point * Point * Point
и функциональных типов:
type PointMutator = Point->Point
type PathOpMutator = PathOp->PathOp
NB: в этом контексте мутатором является функцией, которая дается объект возвращает новый объект того же типа с измененным в некотором роде содержимым (возможно). я могу написать объект для изменения элементов в последовательности путей, как это:
let pathMutator (mutator:PathOpMutator) path =
path |> Seq.map(mutator)
который создает новую последовательность операций пути создания мутации каждого из них.
Теперь я могу это сделать:
let pathPointMutator (mutator:PointMutator) op =
match op with
| Close -> Close
| MoveTo(pt) -> MoveTo(mutator(pt))
| LineTo(pt) -> LineTo(mutator(pt))
| CurveTo(cp1, cp2, dp) -> CurveTo(mutator(cp1), mutator(cp2), mutator(cp3))
Так что, если я хотел написать код, чтобы компенсировать все содержимое пути, я хотел бы посетить каждый элемент пути применения стратегии смещения:
let pointOffseter offset pt =
{ X = offset.X + pt.X; Y = offset.Y + pt.Y }
let offsetPath offset = pathMutator (pathPointMutator (pointOffsetter offset))
Теперь я использую приложение частичной функции здесь - когда я передаю только смещение в pointOffsetter, я возвращаю функцию Point->Point
, которая добавит смещение смещения к его аргументу. Эта функция, в свою очередь, частично применяется к pathPointMutator, которая возвращает функцию PathOp->PathOp
, которая частично применяется к pathMutator, возвращающему функцию Seq<PathOp>->Seq<PathOp>
.
По существу, я создал способ применения стратегии к каждой операции пути для создания новых действий пути.
Вместо того чтобы использовать интерфейс для определения стратегии, вместо этого я использую строго типизированную функцию для определения стратегии. Концепция та же, но конкретная реализация отличается.
Если вы хотите, чтобы взаимодействовать с другими языками .NET вы можете сделать это с помощью интерфейсов, если вы действительно хотите:
type ICalculate =
abstract member Calculate : float->float->float
type Adder =
interface ICalculate with
member this.Calculate x y = x + y
type Subber =
interface ICalculate with
member this.Calculate x y = x - y
type Responder =
member private this.GetCalculator(op) =
match op with
| '+' = new Adder()
| '-' = new Subber()
| _ -> ivalidArg "op" "op not defined"
member this.Respond op x y =
let calc = GetCalculator(op)
calc x y
Это, конечно, слишком много работы, чтобы сделать расчеты, но ваше внимание должен быть на слепом использовании ICalculator, не зная его деталей реализации.
Теперь, если быть точным в определении стратегии, Responder
является более точным в том, что фактическая стратегия выбрана позднее, тогда как в примере PathOp
это делается априори. Это одна из вещей в шаблонах GoF, которые могут усугубляться, если вы пытаетесь быть точными - многие из шаблонов перекрываются, и ваша фактическая реализация может не иметь прочной таксономии.
Мне нравится вопрос, но он слишком широк. Пожалуйста, сосредоточьтесь на одном вопросе и покажите код, который у вас есть. Это даст нам лучший контекст. Общий вопрос о шаблонах F # должен быть в стороне, а не «мясом» сообщения. –
Этот вопрос просто не подходит философии SOs - он широк и самоуверен - например, вы не можете «динамически» (если это означает вне его определения) степень DU, и это имеет какое-то отношение к [проблеме выражения] (https: //en.wikipedia.org/wiki/Expression_Problem), что приводит к очень длительному обсуждению – Carsten
как разделить: я не собираю данные/функции по * домену *, но их в свой файл (s)/module (s) - у меня никогда не было проблемы с заказом - это просто заставляет вас больше думать о проблеме, когда вы сталкиваетесь с взаимным рекурсивным определением между файлами/пространствами имен/сборками (запах кода IMO) – Carsten