2014-10-27 2 views
4

У меня есть разные метрические реализации на расстоянии строк (все они находятся на C#), например, Levensthein, NeedlemanWunsch, Jaccard и т. Д. Работа в них одинакова в основном; возьмите две строки в качестве входных данных и верните оценку подобия в диапазоне [0,1]. Итак, я планировал сделать эти классы реализуют одинаковый базовый интерфейс, как в следующем примере:Что такое хороший шаблон дизайна для следующей ситуации?

public interface IStringDistanceMetric 
{ 
    //Return a similarity between 0 and 1. 
    double CompareSimilarity(string strA, string strB); 
} 

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

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

IStringDistanceMetric metric; 
    if(metricType == Metric.NeedlemanWunsch) 
    { 
    metric = new NeedlemanWunsch(parametersNW); 
    } 
    else if(metricType == Metric.Levensthein) 
    { 
    metric = new Levensthein(parametersL); 
    } 
    . 
    . 
    . 

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

+3

Знаете ли вы, что собираетесь использовать переднюю панель? Возможно, завод подойдет немного лучше. Таким образом, клиент просто запрашивает экземпляр, а заводский метод содержит этот материал 'if else' и возвращает интерфейс. Это делает предположение, что фабрика может получить доступ к параметрам - она ​​всегда может принять аргумент для них. Также позволяет сохранить шаблон стратегии для реализации интерфейса интерфейса, что приятно. –

+0

Нет ничего плохого в том, как вы пытаетесь это сделать, у вас есть 2 отдельных алгоритма для проверки этих двух строк. Наличие их в разных функциях и использование if's, чтобы выбрать, какой из них вы должны использовать, - правильный способ сделать это. – Vajura

+0

Согласовано с @AdamHouldsworth. Кроме того, я бы предположил, что ваш клиентский код имеет интерфейс «stringDistanceMetric» _typed_ в качестве интерфейса (а не как фактический тип класса), поэтому вы можете вводить разные типы конкретных типов. – heltonbiker

ответ

2

Я думаю, что это сильно зависит от того, как он используется; являются ли значения этих параметров фиксированными или должны ли они предоставляться при сравнении? Если число возможных значений для этих параметров является очень низким, дизайн используется в .NET для StringComparers может последовать (любезно sourceof.net):

private static readonly StringComparer _invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, false);   
private static readonly StringComparer _invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, true);  
private static readonly StringComparer _ordinal = new OrdinalComparer(false); 
private static readonly StringComparer _ordinalIgnoreCase = new OrdinalComparer(true);   

public static StringComparer InvariantCulture { 
    get { 
     return _invariantCulture; 
    } 
} 

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

+0

+1 Я не думал об этом решении, и это действительно хорошо. У этого есть проблема, что вызывающий должен знать детали реализации, что я думал, что OP хотел бы избежать. Но это всего лишь мои два цента. –

+0

Я думаю, что может быть необходимость часто менять параметры. Таким образом, в общем, у меня должен быть класс, например: «StringMetricFactory», который принимает все соответствующие параметры и создает конкретный экземпляр класса метрики, который я хочу использовать, и возвращать его, например, как ссылку на интерфейс IStringDistanceMetric. Это подходящее решение? –

+0

Я так думаю или создаю метод, который возвращает запрошенные данные и «вставляет» это в «калькулятор», например, используя «Func GetMeThisMetrics». –

1

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

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

Смежные вопросы