- 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();
}
}
}
কোন মন্তব্য নেই:
একটি মন্তব্য পোস্ট করুন