Click here to Skip to main content
15,867,594 members
Articles / Programming Languages / C#
Tip/Trick

Multiton Design Pattern Using Concurrent Dictionary

Rate me:
Please Sign up or sign in to vote.
2.79/5 (5 votes)
3 Feb 2019CPOL2 min read 11.3K   1   6
This tip describes one of the beneficial design patterns that is named Multiton. Multiton pattern is used when we want to have a singleton instance of our class for each key.

Introduction

Singleton is one of the frequently useful design patterns. However, singleton provides only one instance object of a class in all lifecycle of the program. If we want to generate more instances of a class in some special circumstances, for example against a key like an IP address, we cannot use the singleton pattern. Suppose we want to use an FTP library to download and upload some files from or to some devices. We want to have an instance of the library against an IP address that is assigned to each device. And we want to keep the connection of the FTP alive to use multiple times for download or upload during the lifecycle of the program to reduce the overhead of the Connect method of the FTP connection, instead of make a new connection for each download or upload and kill the connection at the end of each download or upload. In such scenarios, we can use the Multiton design pattern. One of the usable thread-safe implementations of this pattern is available with utilizing the ConcurrentDictionary.        

Using the Code

The class diagram is as shown in the image below:

Multiton Class Diagram

The multiton class code is:

C#
public sealed class Multiton:IDisposable
    {
        private static readonly ConcurrentDictionary<int,Multiton> 
                Multitons=new ConcurrentDictionary<int, Multiton>();
        private readonly FTPLibrary _ftpClient;
        private readonly int _ipAddress;

        bool _disposed = false;
        private Multiton(int ipAddress)
        {
            _ftpClient=new FTPLibrary(ipAddress);
            _ipAddress = ipAddress;
        }

        public static Multiton GetMultitonInstance(int ipAddress)
        {
            return Multitons.GetOrAdd(ipAddress, key => new Multiton(key));
        }

        public void Download(string localAddress, string remoteAddress)
        {
            _ftpClient.Download(localAddress,remoteAddress);
        }

        public void Upload(string localAddress, string remoteAddress)
        {
            _ftpClient.Upload(localAddress,remoteAddress);
        }

        public void Close()
        {
            _ftpClient.Close();
            Multiton multiton;
            Multitons.TryRemove(_ipAddress, out multiton);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (_disposed)
                return;

            if (disposing)
            {
                _ftpClient?.Close();
            }

            _disposed = true;
        }
        ~Multiton()
        {
            Dispose(true);
        }
    }

The ConcurrentDictionary is used to keep the instances of the multiton class. Each instance is created if the key (ipAddress) does not exist in the collection. If the key exists, the GetMultitonInstance method returns the existed instance of the class. The class should implement IDisposable and the dispose pattern to dispose of ftpClient unused instances. These garbage instances will be generated if multi threads call the GetMultitonInstance method at the same time with the same key.  

Therefore, each instance of the multiton has their own FTPLibrary that is initialized in the private constructor. And each instance can be used to Download or Upload using FTP to any specified device. 

The source code includes a sample ftpLibrary class. In the main implementation, I used the FluentFTP library.

Points of Interest

The overhead of Connect() method in FTP libraries is important. The benefit of this implementation is to keep alive the FTP communication until the program calls the close method. In addition, ConcurrentDictionary gives us the benefit of adding devices with specified IP addresses concurrently in the initialization of the dictionary. 

History

  • 4th February, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer MAPNA
Iran (Islamic Republic of) Iran (Islamic Republic of)
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionLeaking resources Pin
Chris Copeland30-Jan-19 3:49
mveChris Copeland30-Jan-19 3:49 
In addition to the comments that were previously made, you have a leak in your example code. If you look at your GetMultitonInstance method you appear to always allocate a new instance of Multiton:
C#
return Multitons.GetOrAdd(ipAddress,new Multiton(ipAddress));

Instead you should be using the factory pattern to prevent the object from being created prior to the existence check:
C#
return Multitons.GetOrAdd(ipAddress, key => new Multiton(key));

Also, how does this address concerns like IDisposable where the instance may need releasing at some point? Perhaps a Shutdown() method or similar which handles disposing of resources?

AnswerRe: Leaking resources Pin
Vahid Asbaghi2-Feb-19 18:18
professionalVahid Asbaghi2-Feb-19 18:18 
QuestionSome issues... Pin
Paulo Zemek28-Jan-19 11:32
mvaPaulo Zemek28-Jan-19 11:32 
AnswerRe: Some issues... Pin
Vahid Asbaghi28-Jan-19 18:35
professionalVahid Asbaghi28-Jan-19 18:35 
GeneralRe: Some issues... Pin
Paulo Zemek28-Jan-19 18:57
mvaPaulo Zemek28-Jan-19 18:57 
GeneralRe: Some issues... Pin
Vahid Asbaghi28-Jan-19 19:18
professionalVahid Asbaghi28-Jan-19 19:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.