接口隔离框架和模式
Interface Segregation Framework and Pattern
我正在编写一个应用程序来处理来自页面的一堆代码数据。我正在使用的主要 class 称为 Instrument,用于存储与任何仪器相关的所有相关数据。数据是从网站下载并解析的。
class Instrument
{
string Ticker {get; set;}
InstrumentType Type {get; set;}
DateTime LastUpdate {get; set;}
}
我的问题是我不确定如何正确构建处理数据解析的 classes。我不仅需要解析数据以填充许多不同的字段(Tickers、InstrumentType、Timestamps 等),而且因为数据是从各种来源提取的,所以没有一种标准模式可以处理所有解析。甚至有一些解析方法需要使用较低级别的解析方法(我正则表达式从字符串中解析 stock/type/timestamp,然后需要单独解析组匹配的情况)。
我最初的尝试是创建一个大的 class ParsingHandler,其中包含一系列方法来处理每个特定的解析细微差别,并将其作为一个字段添加到Instrument class,但我发现很多时候,随着项目的发展,我被迫要么添加方法,要么添加参数来适配class新的不可预见的情况。
class ParsingHandler
{
string GetTicker(string haystack);
InstrumentType GetType(string haystack);
DateTime GetTimestamp(string haystack);
}
在尝试采用更以接口为中心的设计方法之后,我尝试了另一种方法并定义了这个接口:
interface IParser<outParam, inParam>
{
outParam Parse(inParam data);
}
然后使用该接口我定义了一堆解析 classes 来处理每个特定的解析情况。例如:
class InstrumentTypeParser : IParser<InstrumentType, string>
{
InstrumentType Parse(string data);
}
class RegexMatchParser : IParser<Instrument, Match> where Instrument : class, new()
{
public RegexMatchParser(
IParser<string, string> tickerParser,
IParser<InstrumentType, string> instrumentParser,
IParser<DateTime, string> timestampParser)
{
// store into private fields
}
Instrument Parser(Match haystack)
{
var instrument = new Instrument();
//parse everything
return instrument;
}
}
这似乎工作正常,但我现在的情况是我似乎有大量的实现需要传递给 class 构造函数。它似乎危险地接近于不可理解。我对处理它的想法是现在定义枚举和字典来容纳所有特定的解析实现,但我担心它是不正确的,或者我使用这种细粒度的方法通常会走错路。我的方法是否过于细分?像我最初那样用一大堆方法进行一个主要的解析 class 会更好吗?对于这种特定类型的情况是否有替代方法?
我不同意尝试使解析器如此通用,如 IParser<TOut, TIn>
。我的意思是,像 InstrumentParser
这样的东西看起来足以处理乐器。
无论如何,当您解析不同的东西时,例如来自 Match 对象的日期和类似的东西,那么您可以应用一种处理通用参数的有趣技术。也就是说,在您知道要解析的内容的情况下,您可能希望 no 泛型参数(例如 string
到 Instrument
- 为什么要有泛型?)。在那种情况下,您可以定义特殊接口 and/or classes 并减少通用参数列表:
interface IStringParser<T>: IParser<T, string> { }
无论如何,您可能都会从字符串中解析数据。在这种情况下,您可以提供一个通用的 class 来解析 Match 对象:
class RegexParser: IStringParser<T>
{
Regex regex;
IParser<T, Match> parser;
public RegexParser(Regex regex, IParser<T, Match> containedParser)
{
this.regex = regex;
this.parser = containedParser;
}
...
T Parse(string data)
{
return parser.Parse(regex.Match(data));
}
}
通过重复应用此技术,您可以使最消耗的 classes 仅依赖于非泛型接口或具有一个泛型成员的接口。中间 classes 会环绕更复杂(和更具体)的实现,而这一切都只是一个配置问题。
我们的目标始终是尽可能简单地消费 class。因此,尽量包装细节并将它们隐藏在远离消费者的地方。
我正在编写一个应用程序来处理来自页面的一堆代码数据。我正在使用的主要 class 称为 Instrument,用于存储与任何仪器相关的所有相关数据。数据是从网站下载并解析的。
class Instrument
{
string Ticker {get; set;}
InstrumentType Type {get; set;}
DateTime LastUpdate {get; set;}
}
我的问题是我不确定如何正确构建处理数据解析的 classes。我不仅需要解析数据以填充许多不同的字段(Tickers、InstrumentType、Timestamps 等),而且因为数据是从各种来源提取的,所以没有一种标准模式可以处理所有解析。甚至有一些解析方法需要使用较低级别的解析方法(我正则表达式从字符串中解析 stock/type/timestamp,然后需要单独解析组匹配的情况)。
我最初的尝试是创建一个大的 class ParsingHandler,其中包含一系列方法来处理每个特定的解析细微差别,并将其作为一个字段添加到Instrument class,但我发现很多时候,随着项目的发展,我被迫要么添加方法,要么添加参数来适配class新的不可预见的情况。
class ParsingHandler
{
string GetTicker(string haystack);
InstrumentType GetType(string haystack);
DateTime GetTimestamp(string haystack);
}
在尝试采用更以接口为中心的设计方法之后,我尝试了另一种方法并定义了这个接口:
interface IParser<outParam, inParam>
{
outParam Parse(inParam data);
}
然后使用该接口我定义了一堆解析 classes 来处理每个特定的解析情况。例如:
class InstrumentTypeParser : IParser<InstrumentType, string>
{
InstrumentType Parse(string data);
}
class RegexMatchParser : IParser<Instrument, Match> where Instrument : class, new()
{
public RegexMatchParser(
IParser<string, string> tickerParser,
IParser<InstrumentType, string> instrumentParser,
IParser<DateTime, string> timestampParser)
{
// store into private fields
}
Instrument Parser(Match haystack)
{
var instrument = new Instrument();
//parse everything
return instrument;
}
}
这似乎工作正常,但我现在的情况是我似乎有大量的实现需要传递给 class 构造函数。它似乎危险地接近于不可理解。我对处理它的想法是现在定义枚举和字典来容纳所有特定的解析实现,但我担心它是不正确的,或者我使用这种细粒度的方法通常会走错路。我的方法是否过于细分?像我最初那样用一大堆方法进行一个主要的解析 class 会更好吗?对于这种特定类型的情况是否有替代方法?
我不同意尝试使解析器如此通用,如 IParser<TOut, TIn>
。我的意思是,像 InstrumentParser
这样的东西看起来足以处理乐器。
无论如何,当您解析不同的东西时,例如来自 Match 对象的日期和类似的东西,那么您可以应用一种处理通用参数的有趣技术。也就是说,在您知道要解析的内容的情况下,您可能希望 no 泛型参数(例如 string
到 Instrument
- 为什么要有泛型?)。在那种情况下,您可以定义特殊接口 and/or classes 并减少通用参数列表:
interface IStringParser<T>: IParser<T, string> { }
无论如何,您可能都会从字符串中解析数据。在这种情况下,您可以提供一个通用的 class 来解析 Match 对象:
class RegexParser: IStringParser<T>
{
Regex regex;
IParser<T, Match> parser;
public RegexParser(Regex regex, IParser<T, Match> containedParser)
{
this.regex = regex;
this.parser = containedParser;
}
...
T Parse(string data)
{
return parser.Parse(regex.Match(data));
}
}
通过重复应用此技术,您可以使最消耗的 classes 仅依赖于非泛型接口或具有一个泛型成员的接口。中间 classes 会环绕更复杂(和更具体)的实现,而这一切都只是一个配置问题。
我们的目标始终是尽可能简单地消费 class。因此,尽量包装细节并将它们隐藏在远离消费者的地方。