শুক্রবার, ১৫ নভেম্বর, ২০১৩

Open/Closed principle (OCP)


  • Software module mg~n modification Gi Rb¨ closed wKš‘ extension Gi Rb¨ open _vK‡e A_©vr hw` existing trusted class/method G  byZb †Kvb feature add Ki‡Z nq Z‡e inheritance  Gi gva¨‡g add Ki‡Z n‡e existing wU modify K‡i bv|

Real World Comparison




·         g‡bKwi GKwU 2 Zjv N‡ii 1g I 2q Zjvi ga¨eZ©x ¯’v‡b Av‡iv GKwU floor add Ki‡Z n‡e|
·         Bnv wK m¤¢e?
·         hw` m¤¢e nq Z‡e Bnv wK feasible?
·         wb‡¤œ KwZcq option  †`qv n‡jv-
    • cÖ_g option n‡jv Avgiv GKwU 3 Zjv building Gi KvR ïiæ Ki‡Z cvwi hvi 1g Zjv I 3q Zjvi KvR cy‡ivcywi m¤úbœ Ki‡ev wKš‘ 2q Zjv empty ivL‡ev| cieZ©x‡Z Avgiv †h †Kvb mgq 2q Zjvi KvR m¤úbœ Ki‡ev|
    • 2q option n‡jv Avgiv current second floor †f‡½ Avevi `yBwU new floor build Ki‡ev| Z‡e GB optionwU sensible bv|

Identify Problem in Programming

DocumentSearcher

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public class DocumentSearcher : ISearcher<Document>
{
    /// <summary>
    /// Search the repository and return the matches.
    /// </summary>
    /// <param name="criteria">The criteria for the search. Null means to return all documents.</param>
    /// <returns>An IEnumerable of matching objects.</returns>
    public IEnumerable<Document> Search(ISearchCriteria<Document> criteria)
    {
        using (var db = new SolidDemoEntities())
        {
            var query = db.Documents.AsQueryable();
            if (criteria != null)
                query = query.Where(criteria.Expression);
            return query.ToArray();
        }
    }
}

  • g‡bKwi Avgv‡`i new requirement n‡”Q mKj search Gi log Kiv| 
  • GB‡ÿ‡Î Avgiv cÖ_‡g wPš‘v Ki‡ev DocumentSearcher.Search G logging add Ki‡ev| nq‡Zv G‡ÿ‡Î GKwU optional bool _vK‡e hvnv Øviv actually log n‡”Q wK bv Zvnv Rvbv hv‡e|









·         GB ai‡bi approach mg~n bad approach KviY-
    • The DocumentSearcher class would have the additional, unrelated reponsibility of logging. This would violate the Single Responsibility Principle that we just worked so hard to implement.
    • DocumentSearcher class Gi wbR¯^ responsibility Gi mv‡_ byZb unrelated responsibility add n‡e (logging responsibility). d‡j Single Responsibility Principle violate n‡e|
    • Logging code Ab¨‡Kv_vI Use Gi Rb¨ reusable n‡e bv KviY DocumentSearcher class Gi Search method G Logging Gi code Kiv ZvB Logging code Use Ki‡Z †M‡j cy‡iv Search method Use Ki‡Z n‡e
    • If we ever decide not to log, our method has "junk DNA" -- irrelevant, useless code.
o    hw` cieZ©x‡Z KL‡bv decide Kiv nq †h Logging Avi _vK‡e bv Z‡e cy‡iv Search method Avevi change Ki‡Z n‡e wKsev Search method wU ev` w`‡q byZb K‡i  Search method Ki‡Z n‡e

Solution which will not violate OCP

  • Better approach n‡jv DocumentSearcher class ‡K GKwU new class LoggingSearcher Øviv wrap Kiv
  • Bnv Decorator Pattern
  • Existing class  ‡K Ab¨ GKwU class (hvnv Existing class  Gi functionality mg~n‡K extend K‡i Ggb GKwU class) Øviv wrap KivB Decorator Pattern Gi responsibility.
  • GRb¨ Avgiv GLb IQueryLogger interface create Ki‡ev| GB interface Implementation Gi gva¨‡g Avgiv database G logging Ges file G logging Ki‡Z cvi‡ev|
Here's the idea in UML.





  •  wb‡¤œv³ code G Avgiv †`L‡Z cvB LoggingSearcher Gi constructor ISearcher<T> †K argument wnmv‡e ‡bq hvnv class wrap K‡i| GQvov IQueryLogger †K I Bnv A_©vr LoggingSearcher Gi constructor argument AvKv‡i †bq| IQueryLogger actual logging Gi KvR K‡i|
  •  Bnv Dependency Injection pattern
  • GLb DocumentSearcher class wU †K LoggingSearcher Øviv decorate K‡i
  •  For now, the point is that we considered our DocumentSearcher class to be closed for modification, but open for extension. 
  •  LoggingSearcher class wU ISearcher<T> †_‡K inherited
  • DocumentSearcher class wU ISearcher<T> †_‡K inherited
  •  d‡j Avgv‡`i code Liskov Substitution Principle I follow K‡i|

LoggingSearcher

/// <summary>
/// Decorates a wrapped ISearcher with logging.
/// </summary>
/// <typeparam name="T"></typeparam>
public class LoggingSearcher<T> : ISearcher<T>
{
    #region Fields
    ISearcher<T> _wrappedSearcher;
    IQueryLogger _logger;
    #endregion

    #region Properties
    public const string SearcherDependency = "LoggingSearcher_WrappedSearcher";
    #endregion

    #region Constructor
    /// <summary>
    /// Constructor.
    /// </summary>
    /// <param name="wrappedSearcher">The underlying searcher.</param>
    /// <param name="logger">The logger, or null if logging is not desired.</param>
    public LoggingSearcher(
        [Dependency(SearcherDependency)] ISearcher<T> wrappedSearcher,
        IQueryLogger logger)
    {
        Contract.Requires(wrappedSearcher != null);

        _wrappedSearcher = wrappedSearcher;
        _logger = logger;
    }
    #endregion

    #region ISearcher<T> Implementation
    /// <summary>
    /// Search the repository and return the matches.
    /// </summary>
    /// <param name="criteria">The criteria for the search.
    /// Null means to return all records.</param>
    /// <returns>An IEnumerable of matching objects.</returns>
    IEnumerable<T> ISearcher<T>.Search(ISearchCriteria<T> criteria)
    {
        if (_logger != null)
            _logger.Log(WindowsIdentity.GetCurrent().User, criteria.FriendlyString);
        return _wrappedSearcher.Search(criteria);
    }
    #endregion
}

ISearcher

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
/// Describes a class that can search through a repository of objects of type T.
/// </summary>
public interface ISearcher<T>
{
    /// <summary>
    /// Search the repository and return the matches.
    /// </summary>
    /// <param name="criteria">The criteria for the search.
    /// Null means to return all records.</param>
    /// <returns>An IEnumerable of matching objects.</returns>
    IEnumerable<T> Search(ISearchCriteria<T> criteria);
}

IQueryLogger

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
/// Interface for classes that can log queries.
/// </summary>
public interface IQueryLogger
{
    /// <summary>
    /// Log a query with the given criteria by the given user.
    /// Use the current UTC time.
    /// </summary>
    /// <param name="sid">The user's SID.</param>
    /// <param name="criteria">The criteria that are being logged.</param>
    void Log(SecurityIdentifier sid, string criteria);
}

QueryLogger

/// <summary>
/// Logs search criteria (in string form) to an
/// EntityFramework database.
/// </summary>
public sealed class QueryLogger : IQueryLogger
{
    /// <summary>
    /// Log a query with the given criteria by the given user.
    /// Use the current UTC time.
    /// </summary>
    /// <param name="sid">The user's SID.</param>
    /// <param name="criteria">The criteria that are being logged.</param>
    public void Log(SecurityIdentifier sid, string criteria)
    {
        using (var entities = new SolidDemoEntities())
        {
            entities.QueryLogs.Add(new QueryLog()
            {
                Criteria = criteria,
                Sid = sid.ToString(),
                TimeUtc = DateTime.UtcNow
            });
            entities.SaveChanges();
        }
    }
}
 

কোন মন্তব্য নেই:

একটি মন্তব্য পোস্ট করুন