Background

I often write software for systems that are responsible for testing manufactured products. For every product that gets tested, we have to generate a report for the test-results. The way that we do it now is that we create a .txt file with the test-results and write that file into some folder on our network drive. Ultimately, I am looking to move toward a database solution such as SQL.

Since I know that we will eventually be moving towards some kind of database solution, I would like to segregate the current functionality (writing the .txt file to the network drive) by an interface. Eventually I will be able to write a library to control the logging of the test-results to some database. When that happens I would like to simply implement the same interface that the current test-result logging solution will use.

As for the interface, I was thinking to have some simple methods that I know I'll need such as:

LogResults(string operator, string product, string serialNumber, string Results)
Connect(string pathToDriveLocation)

The only thing is, I've never done a database before and I'm not sure if I can adapt the two different ways of performing the logging to the same interface.

Question

Am I thinking about this correctly? Does this seem like a sound approach, or does it need some work?

有帮助吗?

解决方案

I'd like to take @TulainsCordova's answer one step further.

First, define the interface to be the minimal behavior required to perform logging (I'll use C# as an example):

public interface ILogger
{
    void LogResults(string operatorName, string product, string serialNumber, string Results);
}

That's all you need is a way to log stuff. Anything more and your interface gets polluted with implementation details. Any information that is implementation-specific should be handled as a constructor argument to the concrete class that implements this interface. For instance, TextFileLogger:

public class TextFileLogger : ILogger
{
    public TextFileLogger(string fileName)
    {
        this.fileName = fileName;
    }

    private string fileName;

    public void LogResults(string operatorName, string product, string serialNumber, string Results)
    {
        string message = "...";

        File.WriteAllText(fileName, message, Encoding.UTF8);
    }
}

Later on you decide to log to a database, so you can add another class:

public class SqlServerDatabaseLogger : ILogger
{
    public SqlServerDatabaseLogger(string connectionString)
    {
        this.connectionString = connectionString;
    }

    public void LogResults(string operatorName, string product, string serialNumber, string Results)
    {
        string message = "...";

        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            // INSERT into database
        }
    }
}

In the rest of your application can be decoupled from the kind of logger by utilizing a factory class (and accompanying enum for the logger type):

public enum LoggerType
{
    TextFile = 0,
    SqlServer = 1
}

public class LoggerFactory
{
    public LoggerFactory(LoggerType loggerType)
    {
        this.loggerType = loggerType;
    }

    private ILogger logger;
    private LoggerType loggerType;

    public ILogger Logger
    {
        get
        {
            if (logger == null)
                logger = CreateLogger(loggerType);

            return logger;
        }
    }

    private static ILogger CreateLogger(LoggerType loggerType)
    {
        switch (loggerType)
        {
            case LoggerType.TextFile:
                return new TextFileLogger("path/to/filename");
            case LoggerType.SqlServer:
                return new SqlServerDatabaseLogger("connection string");
            default:
                throw new ArgumentOutOfRangeException("loggerType");
        }
    }
}

So create the logger factory once:

LoggerFactory factory = new LoggerFactory(LoggerType.TextFile);
ILogger logger = factory.Logger;

Pass the logger or factory around to any number of other classes. When switching to a new logging type, just change a single line of code:

LoggerFactory factory = new LoggerFactory(LoggerType.SqlServer);

And you're done.

其他提示

Your approach is good indeed. There should be no conceptual difference (interface-wise) between logging to a text file, to a database table or to a printer (or screen).

In the connect method I suggest you change signature to something more neutral, like:

openDevice(String deviceString);

A devide string cuold be a path to a file...

connect("/var/logs/log001.txt");

or a database connection string...

connect("user/password@10.72.2.10:1021/database");

In the case of file logs you simply validate whether the file exists and you have permission.

You can choose between adding a "throws CouldNotOpenDeviceException" or returning an error code.

void openDevice(String deviceString) throws CouldNotOpenDeviceException;

vs

int openDevice(String deviceString);
许可以下: CC-BY-SA归因
scroll top