The program parses log files - each log file may have different kind of field format (fixed width, comma delimited, etc). Also each log file are mixed of several different kind of logs - each kind have different field definition). For example, the CSV log file may look like

Log file A

logType1, 10/1/2012, 12, abc
logType2, a, b, c, d, 11/1/2012
logType1, 10/2/2012, 21, def
logType2, e, f, c, d, 12/1/2012
logType3, 3.23, ....

The following is code. How many solid-principles were violated in the following code? One guy said the list of layout definition shouldn't be mixed with the parsing log. So it at least violates SRP (Or more)? What's the best way to refactory the structure?

// Field
public interface IField { .... }
public class Field : IField { ... common field methods, etc.... }
public class FixedWidthField : Field { }
public class CommaDelimField : Field { ... }

// Log type
public interface ILogType<out T> where T : IField { ... IEnumerable<T> Fields { get; } }
public class LogType<T> : ILogType<T> where T : IField 
{ .... 
    public LogType(..., List<T> fields) { ... Fields = fields; }
}

// File
public inteface ILogFile<out T> where T: IField { ... IEnumerable<ILogType<T>> LogTypeList { get; set; } }
public abstract class LogFile<T> : ILogFile<T> where T: IField 
{ ....
    public IEnumerable<ILogType<T>> LogTypeList { get; set; }
    public virtual string Row { get { ... } set { ...} }
    public string GetParsedFieldString() { ... }
}
public class CommaDelimLog : LogFile<CommaDelimField>
{
     public override string Row { get { ... } set { ...code to parse the line...} }
     public override string GetParsedFieldString() { ... }
}

// The following initilize code store all the layout information
public static List<ILogFile<IField>> LogFileList = new List<ILogFile<IField>>
{
    new CommaDelimLog("logFileA", ...., new List<ILogType<CommaDelimField>> {
        new LogType<CommaDelimField>("logType1", ... new List<CommaDelimField>{ .... }
        new LogType<CommaDelimField>("logType2", ... new List<CommaDelimField>{ .... }
        ....
    }),
    new CommaDelimLog("logFileB", .... a long long list

The main program get a item from the LogFileList according to the file name pattern, read the log files line by line and assign the property Row and then get the parsed string.

有帮助吗?

解决方案

It probably violates the open-closed principle due to inheritence from LogFile for behaviour (although GetParsedFieldString is not actually virtual in the abstract base, and it's hard to be certain without more context).

You could instead have some kind of parser interface, which several concrete classes implement, and whenever you need a new one, you build a new one. The risk with the LogFile class is that you'll create more subtypes, then start finding shared behaviour, then refactor into several levels of inheritence and it will become a mess where you can't change anything without having to do heaps of testing.

You seem to be mostly asking about the open-closed principle, based on the question's tag, but the Dependency Inversion principle is also being violated in that your concrete classes rely directly on other concrete classes, like CommaDelimLog relying on CommaDelimField.

With ninject, for example, you could do something like:

Bind<IField>().To<CommaDelimField>().WhenInjectedInto<CommaDelimLog>(); 

and then pass the concrete field into the log through its constructor. The different types of field have the same signature, so the CommaDelimLog class definition doesn't need to know directly that it relies on a CommaDelimField.

There might be other violations, but I'll defer to others.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top