Application Code vs Library Code

When writing code I think that it is important to keep in mind what type of code it is. Far too often, I see people writing very complicated structures without the need. I think that a reason for this is that the code was inspired from some library. Unless reading other people’s code for fun, library code and interfaces is what an average developer is most likely to get in touch with, so it is natural to be inspired by it. Unfortunately it can lead very wrong.

Library code is very special; it is meant to be reusable in different applications, under different circumstances. It has to be  extendable and adaptable without code changes.

Writing code whose behaviour can change without code changes is of course challenging and requires wide spread usage of dependency injection.

Application code is just used in one environment and can be changed to alter the behaviour. As an example of the difference, I’ll implement a sample logging mechanism. One written as application code and one written as library code.

The Application Logger

First I’ll show my application specific logger. This is how I would write a logger for one application. It’s straight on and only has two configuration options. One is the file name which is set through the application configuration file. The other one is the debug level which is set as a constant. It has different default values for debug and release builds. The formatting is hard coded, but it is in just one place so it is easy to change if needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public enum ApplicationLoggerSeverity
{
    Error,
    Warning,
    Information,
    Debug
}
 
public static class ApplicationLogger
{
    private static readonly string logFileName;
 
#if DEBUG
    private const ApplicationLoggerSeverity minimumSeverity = 
        ApplicationLoggerSeverity.Debug;
#else
    private const ApplicationLoggerSeverity minimumSeverity = 
        ApplicationLoggerSeverity.Information;
#endif
 
    static ApplicationLogger()
    {
        logFileName = ConfigurationManager.AppSettings["LogFile"];
    }
 
    public static void Write(ApplicationLoggerSeverity severity, 
        string message)
    {
        if (severity >= minimumSeverity)
        {
            using (StreamWriter writer = 
                new StreamWriter(logFileName, true))
            {
                writer.WriteLine("{0} [{1}]: {2}", DateTime.Now, 
                    severity.ToString(), message);
            }
        }
    }
}

A total of 39 lines of code to implement the application logger. To write a message one line of code is needed.

ApplicationLogger.Write(ApplicationLoggerSeverity.Information, "Logging!");

My Logging Library

If I would write a logging library that could be configurable I would make sure there were a lot of extension points and also mocking possibilities. Here comes the code. For the explanation you will have to scroll all the way down past the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public enum LogSeverity
{ 
    Critical,
    Error,
    Warning,
    SystemInformation,
    TransactionInformation,
    SystemDebug,
    TransactionDebug,
    ExtraVerbose
}
 
public interface ILogger
{
    void Write(LogSeverity severity, string message);
}
 
public interface ILogFilterBehaviour
{
    bool Filtered(LogSeverity severity, string message);
}
 
public interface ILogFormatterBehaviour
{
    string Format(LogSeverity severity, string message);
}
 
public interface ILogWriterBehaviour
{
    void WriteLine(string message);
}
 
public class DefaultLogBehaviours : ILogFilterBehaviour, 
    ILogFormatterBehaviour, ILogWriterBehaviour
{
    public static LogSeverity MinimumSeverity = 
        LogSeverity.SystemInformation;
    private static readonly string logFileName;
 
    static DefaultLogBehaviours()
    {
        logFileName = ConfigurationManager.AppSettings["LogFile"];
    }
 
    public bool Filtered(LogSeverity severity, string message)
    {
        return severity > MinimumSeverity;
    }
 
    public string Format(LogSeverity severity, string message)
    {
        return string.Format("{0} [{1}]: {2}", DateTime.Now, 
            severity.ToString(), message);
    }
 
    public void WriteLine(string message)
    {
        using (StreamWriter writer = 
            new StreamWriter(logFileName, true))
        {
            writer.WriteLine(message);
        }
    }
}
 
public class LibraryLogger : ILogger
{
    private ILogFilterBehaviour filter;
    private ILogFormatterBehaviour formatter;
    private ILogWriterBehaviour writer;
 
    public LibraryLogger(ILogFilterBehaviour filter, 
        ILogFormatterBehaviour formatter,
        ILogWriterBehaviour writer)
    {
        this.filter = filter;
        this.formatter = formatter;
        this.writer = writer;
    }
 
    public void Write(LogSeverity severity, string message)
    {
        if (!filter.Filtered(severity, message))
        {
            writer.WriteLine(formatter.Format(severity, message));
        }
    }
}

There it is. 88 lines of code. More than twice the amount needed in the application specific logger. The functionality is the same, but everything is broken out to different behaviour classes. Those dependencies are injected to the actual logger in the constructor of the logger. That’s an important difference too. This logger has to be instantiated. The application logger is all static. The instantiation isn’t exactly a breeze, so in my application using the library logger I’ll write a LogFactory to have the instantiation logic in one place.

89
90
91
92
93
94
95
96
97
98
public static class LogFactory
{
    public static ILogger CreateLogger()
    { 
        DefaultLogBehaviours defaultBehaviours = 
            new DefaultLogBehaviours();
        return new LibraryLogger(defaultBehaviours, 
            defaultBehaviours, defaultBehaviours);
    }
}

Now we finally have an interface that can be used with just one statement.

LogFactory.CreateLogger().Write(LogSeverity.SystemInformation, "Logging!");

Conclusion

The application logger which is coded straight on is 39 lines. The library style logger is a total of 98 lines, including the wrapper. In a real scenario I would probably extend it with even more code, providing my own configuration section instead of using appSettings. I think that both of these loggers are perfectly valid to write, but the choice between the two styles is completely dependent on whether the code goes into a library or not. If the code is for a library, you have to try to think about every possible future use case and make sure it can be handled. There can never be too much flexibility as long as the interface remains reasonable to use. If the code is in your application, don’t make things over complicated. Stick with the easiest possible solution and refactor it as needed later.

  • Leave a Reply

    Your name as it will be displayed on the posted comment.
    Your e-mail address will not be published. It is only used if I want to get in touch during comment moderation.
    Your name will be a link to this address.
Software Development is a Job – Coding is a Passion

I'm Anders Abel, an independent systems architect and developer in Stockholm, Sweden.

profile for Anders Abel at Stack Overflow, Q&A for professional and enthusiast programmers

Code for most posts is available on my GitHub account.

Popular Posts

Archives

Series

Powered by WordPress with the Passion for Coding theme.