Перерасчет на производный тип - PullRequest
3 голосов
/ 21 июня 2011

У меня проблема в том, что я не знаю, как подойти, и я надеюсь, что у людей здесь будут несколько полезных советов.

Я анализирую текстовые файлы, которые содержат несколько журналов (один журналза строку).Формат выглядит примерно так:

Date Type Description
10/20 A LogTypeADescription
10/20 B LogTypeBDescription
10/20 C LogTypeCDescription

Здесь вы можете видеть три «типа» журналов (A, B и C).В зависимости от типа журнала, я буду анализировать поле «Описание» по-разному.

Мой вопрос: как мне настроить структуру данных?Я хотел бы сделать что-то вроде этого:

class Log
{
  DateTime Date;
  String Type;
  String Description;

  public Log(String line)
  {
      Parse(line);
  }
}

class ALog : Log { }
class BLog : Log { }
class CLog : Log { }

Теперь каждый производный класс может иметь свои уникальные свойства, в зависимости от того, как анализируется поле «Описание», и они по-прежнему будут поддерживать три «ядра»."properties (Date, Type и Description).

Пока все хорошо, за исключением того, что я не знаю, какой тип (производного) журнала мне нужен, пока я не проанализирую строку из файла журнала.Конечно, я мог бы проанализировать строку, а затем выяснить это, но я действительно хочу, чтобы код синтаксического анализа был в конструкторе "Log".Я хотел бы сделать что-то вроде этого:

void Parse(String line)
{
   String[] pieces = line.Split(' ');
   this.Date = DateTime.Parse(pieces[0]);
   this.Type = pieces[1];
   this.Description = pieces[2];

   if(this.Type == "A")
     this = new ALog();
   else if(this.Type == "B")
     this = new BLog();
   else if(this.Type == "C")
     this = new CLog();
}

Но, к сожалению, я не думаю, что это возможно.Я еще не пробовал, но я уверен, что делаю это:

Log l = new Log(line);
if(l.Type == "A") l = new ALog();

Было бы или незаконно, или уничтожило бы весь анализ, который я делал, когда я впервые создал «Журнал».

Есть предложения?

Ответы [ 5 ]

2 голосов
/ 21 июня 2011

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

using System;

class Log
{
    DateTime Date;
    String Type;
    String Description;

    public Log(String line)
    {
        String[] pieces = line.Split(' ');
        this.Date = DateTime.Parse(pieces[0]);
        this.Type = pieces[1];
        LogParser parser = GetParser(this.Type);
        this.Description = parser.Parse(pieces[2]);
    }

    static LogParser GetParser(string type)
    {
        switch (type)
        {
            case "A":
                return new AParser();
            case "B":
                return new BParser();
            case "C":
                return new CParser();
            default:
                throw new NotSupportedException();
        }
    }
}

abstract class LogParser { public abstract string Parse(string line);}

class AParser : LogParser { public override string Parse(string line) { /* do parsing for A */ return string.Empty; } }
class BParser : LogParser { public override string Parse(string line) { /* do parsing for B */ return string.Empty; } }
class CParser : LogParser { public override string Parse(string line) { /* do parsing for C */ return string.Empty; } }
2 голосов
/ 21 июня 2011

Удалите конструктор и измените Parse на статический, который возвращает Log.

static Log Parse(string line)
{
     string[] tokens  line.Split(' ');
     var log = null;
     if (tokens[1] == "A") log = new ALog();
     else if (tokens[1] == "B") log = new BLog();
     else log = new CLog();
     log.Date = tokens[0];
     log.Description = tokens[1];
     return log;
}
1 голос
/ 21 июня 2011

Переосмысление подходов Джончама и Бтилли:

using System;

class Log
{
    DateTime Date;
    String Type;
    String Description;
    Dictionary<string,LogParser> logDictionary;

    static Log()
    {
        logDictionary = new Dictionary<string,LogParser>;

        logDictionary.Add("A",new AParser());
        logDictionary.Add("B",new BParser());
        logDictionary.Add("C",new CParser());
    }

    public Log(String line)
    {
        String[] pieces = line.Split(' ');    
        this.Date = DateTime.Parse(pieces[0]);    
        this.Type = pieces[1];
        LogParser parser = GetParser(this.Type);
        this.Description = parser.Parse(pieces[2]);
    }

    static LogParser GetParser(string type)
    {
        return logDictionary<string,LogParser>(type);
    }
}

abstract class LogParser { public abstract string Parse(string line);}

class AParser : LogParser { public override string Parse(string line) { /* do parsing for A */ return string.Empty; } }
class BParser : LogParser { public override string Parse(string line) { /* do parsing for B */ return string.Empty; } }
class CParser : LogParser { public override string Parse(string line) { /* do parsing for C */ return string.Empty; } }
1 голос
/ 21 июня 2011

Еще одно решение. Опусти свой ОО-молот и возьми свой функциональный.

C # имеет словари и анонимные функции. Иметь словарь функций, которые знают, как взять Log и описание, и могут проанализировать эту информацию и поместить ее в Log. Тогда вы просто parseDescription[logType](this, description).

Это означает, что вам нужен словарь с 3 функциями, а не с 3 новыми классами.

Разница не так велика в этом примере. Но подумайте, не было ли в вашей записи 2 разных поля, которые, возможно, придется анализировать несколькими способами. Классовый подход требует 9 новых классов, в то время как словарный подход имеет 2 словаря с 3 функциями в каждом. Если бы их было 3, сравнение составило бы 27 классов против 3 словарей с 3 функциями в каждом.

1 голос
/ 21 июня 2011

Вы можете прочитать строку в разбивке, как вы, затем прочитать «тип» и вызвать Активатор , чтобы создать один из ваших конкретных типов, полученных из вашего (возможно, абстрактного) базового журнала, передав в ваших аргументах split для конструктора, создающего новый конкретный конкретный экземпляр.

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

Конечно, если вы не хотите избегать рефлексии.

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