Memory Leak And The IDisposable Pattern
Sunday morning.
I return to my current project after two weeks that I wasn't there.
The "welcome party" is a story about the crisis of the last two weeks.
Apparently, we have a memory leak and in the past two weeks nobody
solved the problem. The production servers work for five hours and then
the memory usage is going over 900MB and the servers crush and the application
crushes with them. The system administrators make a recycle to the application
and the story goes on again.
Of course the users are angry because it was a new release of the application.
The details I get are that the memory is full of objects that the garbage collector
isn't able to finalize.
After a short brainstorm with two of my colleagues (Ran and Yossi) a lead to the
solution is raised and I find the reason for the memory leak.
We use an enterprise library LogWriter object in order to log messages to our database.
The LogWriter implement the IDisposable interface because two of its members
(LogWriterStructureHolder and ILogWriterStructureUpdater) need to be disposed.
The class that wrapped the LogWriter didn't implement the IDisposable interface
and therefore the memory wasn't released. Also, there is mass usage of the wrapper
class in the application and the result - a memory leak.
The solution was simple. Use the IDisposable pattern in order to dispose the wrapper
class and wrap every usage of the wrapper class with the using clause.
Currently, the memory usage of the servers is 120MB and the problem was solved.
IDisposable Pattern
The IDisposable pattern isn't one of the a classic patterns. It's a pattern suggested in
MSDN to implement the IDisposable interface. You should be familiar with the pattern
or with the interface because it's a basic thing to know about the .Net framework.
How to Use IDisposable Pattern
Lets look at a code example.
public class DisposeObject : IDisposable
{
#region Members
private bool _disposed = false;
#endregion
#region IDisposable Members
~DisposeObject()
{
Dispose(false);
}
/// <summary>
/// Dispose the current object
/// </summary>
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// clean up resources
CleanUp();
// The object will be cleaned up only if the method
// gets true - we are in the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
// Taken from MSDN.
GC.SuppressFinalize(this);
}
// dispose occurred
_disposed = true;
}
}
private void CleanUp()
{
// put here the code to dispose all managed
// and unmanaged resources
}
#endregion
}
The code is self explanatory.
Conclusion
Hope that writing this post will help you think about IDisposable pattern if
you have a memory leak in managed environment like .Net. The reason isn't always
the lack of IDisposable but it's a good start when you look for memory leaks' reason.
Schema Definitions Of EDM In Entity Framework
On the previous post about the entity framework I wrote about the
entity data model (EDM) and explained how to add a one to one mapping
model to your application.
Today I'm going to explain a little about the bits and bites of the new
XML schema types which describe the models.
The Schema Types
The EDM is divided into three models (as explained in my previous post) - the
conceptual, the mapping and the logical model.
Every model has it's own XML schema type:
- Store Schema Definition Language (.ssdl) schema type -
The schema describes the database tables and relations between them.
- Mapping Specification Language (.msl) schema type -
The schema describes the mapping between the conceptual model (csdl)
and the logical model (ssdl).
- Conceptual Schema Definition Language (.csdl) schema type -
The schema describes the objects and their relations.
The Example Model
Lets look at a picture of the example I'm going to show:
I use two tables from a database - Companies and Employees.
The example will help me explain the relevant parts in the schema files of the EDM.
You should note that the example is a simple one to one mapping.
Store Metadata - SSDL Example
<?xml version="1.0" encoding="utf-8"?>
<Schema Namespace="CompaniesModel.Store" Alias="Self"
ProviderManifestToken="09.00.3054"
xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">
<EntityContainer Name="dbo">
<EntitySet Name="Companies" EntityType="CompaniesModel.Store.Companies" />
<EntitySet Name="Employees" EntityType="CompaniesModel.Store.Employees" />
<AssociationSet Name="FK_Employees_Companies"
Association="CompaniesModel.Store.FK_Employees_Companies">
<End Role="Companies" EntitySet="Companies" />
<End Role="Employees" EntitySet="Employees" />
</AssociationSet>
</EntityContainer>
<EntityType Name="Companies">
<Key>
<PropertyRef Name="CompanyID" />
</Key>
<Property Name="CompanyID" Type="bigint" Nullable="false"
StoreGeneratedPattern="Identity" />
<Property Name="CompanyName" Type="nvarchar" MaxLength="100" />
<Property Name="CompanyAddress" Type="nvarchar" MaxLength="100" />
<Property Name="CompanyPhone" Type="nvarchar" MaxLength="50" />
</EntityType>
<EntityType Name="Employees">
<Key>
<PropertyRef Name="EmpolyeeID" />
</Key>
<Property Name="EmpolyeeID" Type="bigint" Nullable="false"
StoreGeneratedPattern="Identity" />
<Property Name="CompanyID" Type="bigint" />
<Property Name="EmployeefirstName" Type="nvarchar" MaxLength="50" />
<Property Name="EmployeeLastName" Type="nvarchar" MaxLength="50" />
<Property Name="EmployeeJobTitle" Type="int" />
<Property Name="EmployeePhone" Type="nvarchar" MaxLength="50" />
</EntityType>
<Association Name="FK_Employees_Companies">
<End Role="Companies" Type="CompaniesModel.Store.Companies"
Multiplicity="0..1" />
<End Role="Employees" Type="CompaniesModel.Store.Employees"
Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Companies">
<PropertyRef Name="CompanyID" />
</Principal>
<Dependent Role="Employees">
<PropertyRef Name="CompanyID" />
</Dependent>
</ReferentialConstraint>
</Association>
</Schema>
The schema define the namespace to use for the store definition which is
CompaniesModel.Store.
Then you can see that the schema is built from three main elements -
the EntityContainer element, the EntityType elements and the Association elements.
The EntityContainer is the definition of all the elements and their associations.
Every EntityType is reference by an EntitySet and every Association is referenced by
AssociationSet. You can see that the association set is using the entity sets defined
earlier and also using the Roles which will be defined later in the Association elements.
The EntityType elements are the definition of every table in the database.
Every EntityType has a Key element that describe the primary key of the current
element. Also every EntityType has Property elements that describe the fields in the
database. For every Property there is a set of attributes which include it's name and
database type and also constraints like MaxLength or Nullable which are taken
from the database.
The Association elements are the definition of the foreign keys between the tables
in the database. Every Association define the end points of the foreign key by
using the End elements. Every End element is built from its Role, its type which is the
table it's connected to and its Multiplicity which define the the number of entities
that can exist on each side of a relationship (cardinality).
There is also the ReferentialConstraint that define the fields that are used in the
association. The ReferentialConstraint is built from Dependent elements which describe
the property that is used from the EntityType.
Mapping Specification - MSL Example
<?xml version="1.0" encoding="utf-8"?>
<Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">
<EntityContainerMapping StorageEntityContainer="dbo"
CdmEntityContainer="CompaniesEntities">
<EntitySetMapping Name="Companies">
<EntityTypeMapping TypeName="IsTypeOf(CompaniesModel.Companies)">
<MappingFragment StoreEntitySet="Companies">
<ScalarProperty Name="CompanyID" ColumnName="CompanyID" />
<ScalarProperty Name="CompanyName" ColumnName="CompanyName" />
<ScalarProperty Name="CompanyAddress" ColumnName="CompanyAddress" />
<ScalarProperty Name="CompanyPhone" ColumnName="CompanyPhone" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="Employees">
<EntityTypeMapping TypeName="IsTypeOf(CompaniesModel.Employees)">
<MappingFragment StoreEntitySet="Employees">
<ScalarProperty Name="EmpolyeeID" ColumnName="EmpolyeeID" />
<ScalarProperty Name="EmployeefirstName" ColumnName="EmployeefirstName" />
<ScalarProperty Name="EmployeeLastName" ColumnName="EmployeeLastName" />
<ScalarProperty Name="EmployeeJobTitle" ColumnName="EmployeeJobTitle" />
<ScalarProperty Name="EmployeePhone" ColumnName="EmployeePhone" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<AssociationSetMapping Name="FK_Employees_Companies"
TypeName="CompaniesModel.FK_Employees_Companies" StoreEntitySet="Employees">
<EndProperty Name="Companies">
<ScalarProperty Name="CompanyID" ColumnName="CompanyID" />
</EndProperty>
<EndProperty Name="Employees">
<ScalarProperty Name="EmpolyeeID" ColumnName="EmpolyeeID" />
</EndProperty>
<Condition ColumnName="CompanyID" IsNull="false" />
</AssociationSetMapping>
</EntityContainerMapping>
</Mapping>
The msl schema define the relation between the ssdl schema and the csdl schema.
The main element is the EntityContainerMapping which connect the ssdl container
(the StorageEntityContainer) and the csdl container (the CdmEntityContainer).
The mapping between tables and entities are based on the EntitySetMapping elements.
The mapping between foreign keys and entity relations are based on the
AssociationSetMapping elements.
The EntitySetMapping element has a main element which is the EntityTypeMapping.
The EntityTypeMapping has a type name which was described in the csdl file. That
type name is mapped to the one or more MappingFragment which describe the store entity set.
This way of definition enable us to map entities to more then one table as will be shown
in the next posts I'm going to write.
Also there is a mapping for every property in the csdl to its property in the store
definition by the ScalarProperty element.
The AssociationSetMapping element maps the store entity set which was defined in the
ssdl file to the relation that was defined in the csdl file. The TypeName attribute describe
the name of the csdl relation and the StoreEntitySet describe the name of the
ssdl association. Every EndProperty element is the mapping between the End elements
in the ssdl to the End elements in the csdl. The Condition is the constraint which in our case
CompanyID shouldn't be null in order for the association to be valid.
Conceptual Schema - CSDL Example
<?xml version="1.0" encoding="utf-8"?>
<Schema Namespace="CompaniesModel" Alias="Self"
xmlns=http://schemas.microsoft.com/ado/2006/04/edm>
<EntityContainer Name="CompaniesEntities">
<EntitySet Name="Companies" EntityType="CompaniesModel.Companies" />
<EntitySet Name="Employees" EntityType="CompaniesModel.Employees" />
<AssociationSet Name="FK_Employees_Companies"
Association="CompaniesModel.FK_Employees_Companies">
<End Role="Companies" EntitySet="Companies" />
<End Role="Employees" EntitySet="Employees" />
</AssociationSet>
</EntityContainer>
<EntityType Name="Companies">
<Key>
<PropertyRef Name="CompanyID" />
</Key>
<Property Name="CompanyID" Type="Int64" Nullable="false" />
<Property Name="CompanyName" Type="String" MaxLength="100" />
<Property Name="CompanyAddress" Type="String" MaxLength="100" />
<Property Name="CompanyPhone" Type="String" MaxLength="50" />
<NavigationProperty Name="Employees"
Relationship="CompaniesModel.FK_Employees_Companies"
FromRole="Companies" ToRole="Employees" />
</EntityType>
<EntityType Name="Employees">
<Key>
<PropertyRef Name="EmpolyeeID" />
</Key>
<Property Name="EmpolyeeID" Type="Int64" Nullable="false" />
<Property Name="EmployeefirstName" Type="String" MaxLength="50" />
<Property Name="EmployeeLastName" Type="String" MaxLength="50" />
<Property Name="EmployeeJobTitle" Type="Int32" />
<Property Name="EmployeePhone" Type="String" MaxLength="50" />
<NavigationProperty Name="Companies"
Relationship="CompaniesModel.FK_Employees_Companies"
FromRole="Employees" ToRole="Companies" />
</EntityType>
<Association Name="FK_Employees_Companies">
<End Role="Companies" Type="CompaniesModel.Companies"
Multiplicity="0..1" />
<End Role="Employees" Type="CompaniesModel.Employees"
Multiplicity="*" />
</Association>
</Schema>
The schema describe the namespace for the conceptual model which is CompaniesModel.
As in the ssdl schema the csdl schema is divided into three main elements -
the EntityContainer element, the EntityType elements and the Association elements.
The EntityContainer is the definition of all the entities and their relations.
Every EntityType is reference by an EntitySet and every Association is referenced by
AssociationSet. You can see that the association set is using the entity sets defined
earlier and also using the Roles which will be defined later in the Association elements.
The EntityType element is the definition of every entity.
Every EntityType has a Key element that describe the primary property of the current
element (The entity ID). Also every EntityType has Property elements that describe its
properties. For every Property there is a set of attributes which include it's name and
.Net type and also constraints like MaxLength or Nullable.
There is an element called NavigationProperty that describe relations between
entities. In our example every employee works in a company which is another entity
in the schema. The FromRole and ToRole describe the direction of the relation which
will be described in the Association element (one to one, one to many, many to many).
The Association element is the definition of the relations of the entities.
Every Association define the end points of the relation by using the End elements.
Every End element is built from its Role, its type which is the
entity it's connected to and its Multiplicity which define the the number of entities
that can exist on each side of a relationship (cardinality).
Summary
Let sum up what I showed in the post.
I described the three models which construct the EDM and the XML schema types.
I showed simple examples for every schema file and described important key
features in the files. I didn't show stored procedure mapping or mapping of one entity
to many tables. These subjects will be explained in the next posts.
As you can understand, the mission of manually writing the schema files is very
complicated but if you want to understand the entity framework you should be
familiar with them.
In the next post about the entity framework I'm going to explain how to work with
other features the framework provides.
Singleton Pattern
The first pattern in the creational pattern series is the singleton pattern.
You can read my previous posts about design patterns here:
Structural patterns
Decorator pattern
Proxy pattern
Facade pattern
Adapter pattern
Composite pattern
Bridge pattern
Flyweight pattern
Introduction
The singleton pattern is a very useful pattern.
I encountered it while I was a second year student in an object oriented
course I took. During that course we had a lecture about design patterns and
the singleton pattern was one of the patterns that were discussed.
Think about it, one lecture about design patterns in a course about object oriented...
What is the Singleton Pattern?
The singleton pattern ensures that a class has one instance only.
Also, it provides the access point to the get the that single instance.
For a UML diagram of the pattern go to dofactory site.
Example in C#
How does it works? lets look at an example:
class Singleton
{
#region Members
// the only instance of the singleton object
private static Singleton _me;
// lock synchronization object
private static object syncLock = new object();
#endregion
#region Properties
/// <summary>
/// Get the instance of the singleton
/// </summary>
public static Singleton Instance
{
get
{
// handle the creation only once and if
// needed also the implementation is
// thread safe
if (_me == null)
{
lock (syncLock)
{
if (_me == null)
{
Singleton newInstance = new Singleton();
System.Threading.Thread.MemoryBarrier();
_me = newInstance;
}
}
}
return _me;
}
}
#endregion
#region Ctor
private Singleton()
{
}
#endregion
The implementation is thread safe. I lock the syncLock object and then create
the instance of the singleton only once. After that, the instance will be returned
every time it is being asked by the Instance property.
I added the MemoryBarrier call on the thread to correct the implementation
I made earlier thanks to the comment of Arnon. Look at the post of Eran Kampf for details.
Summary
To sum up the post, you should use the pattern whenever you need to be sure that only
single instance of a class is created. As I wrote the singleton is very useful.
Be aware that using a static object (the singleton object) has a performance issue
(because it is static) and therefore use the pattern carefully.
The next stop in the creational patterns - the abstract factory.
Creational Patterns
In my first post about design patterns I wrote that there are three kinds of
design patterns. I covered the first kind ,the structural patterns, in the previous posts.
You can read my previous posts on structural patterns here:
Decorator pattern
Proxy pattern
Facade pattern
Adapter pattern
Composite pattern
Bridge pattern
Flyweight pattern
Today I'm going to start the journey and dive into the creational patterns.
What are Creational Patterns?
Creational patterns deal with the instantiation process.
Their main issue is to separate the application from the way objects are created, composed
or represented.
There are two common subjects in the creational patterns -
- They encapsulate the knowledge about which classes the application use.
- They hide the way the classes are being built or composed.
The patterns are close related and therefore you may choose between them
when you design your application. It's very important to know the cons and pros of
every pattern in order to make the right decision (this is right for all the design patterns).
Types of Creational Patterns
The creational patterns include the following patterns:
- Singleton
- Abstract Factory
- Builder
- Prototype
- Factory Method
In the next post I'm going to write about the first creational pattern - the singleton.
Flyweight Pattern
In the last post about structural patterns I'm going to explain how
and when you should use the flyweight pattern.
You can read my previous posts on structural patterns here:
Decorator pattern
Proxy pattern
Facade pattern
Adapter pattern
Composite pattern
Bridge pattern
What is the Flyweight Pattern?
The flyweight pattern is used whenever you have large amount of small objects
that share common information.
The use of the pattern reduces the storage of those objects.
The flyweight distinguishes between inner state (sometime called intrinsic state)
and outer state (sometime called extrinsic state) of the object.
The inner state can be shared by the objects and therefore minimize the amount
of storage needed by the objects.
The outer state can be computed or stored outside the objects and are given to
the objects whenever it's needed.
For a UML diagram of the pattern go to dofactory site.
Flyweight C# Example
Lets look at an example for the use of flyweight pattern:
#region Helper
public enum eNumbers
{
One,
Two,
Three,
// .. and so on with every number
}
#endregion
#region The Flyweight Factory
public class NumbersFactory
{
#region Members
private Dictionary<eNumbers, Number> _numbers =
new Dictionary<eNumbers, Number>();
#endregion
#region Methods
public Number GetNumber(eNumbers number)
{
if (!_numbers.ContainsKey(number))
{
switch (number)
{
case (eNumbers.One):
{
_numbers.Add(number, new One());
break;
}
case (eNumbers.Two):
{
_numbers.Add(number, new Two());
break;
}
case (eNumbers.Three):
{
_numbers.Add(number, new Three());
break;
}
// ... and so on with every number
default:
{
break;
}
}
}
return _numbers[number];
}
#endregion
}
#endregion
#region The Flyweight
public abstract class Number
{
#region Members
protected int _number;
protected string _numberName;
protected int _numberSize;
#endregion
#region Methods
public abstract void WriteNumber(int numberSize);
#endregion
}
#endregion
#region The Flyweight Concrete
public class One : Number
{
#region Ctor
public One()
{
this._number = 1;
this._numberName = "One";
}
#endregion
#region Methods
public override void WriteNumber(int numberSize)
{
this._numberSize = numberSize;
Console.WriteLine(string.Format("{0} is size {1}",
_numberName, _numberSize));
}
#endregion
}
public class Two : Number
{
#region Ctor
public Two()
{
this._number = 2;
this._numberName = "Two";
}
#endregion
#region Methods
public override void WriteNumber(int numberSize)
{
this._numberSize = numberSize;
Console.WriteLine(string.Format("{0} is size {1}",
_numberName, _numberSize));
}
#endregion
}
public class Three : Number
{
#region Ctor
public Three()
{
this._number = 3;
this._numberName = "Three";
}
#endregion
#region Methods
public override void WriteNumber(int numberSize)
{
this._numberSize = numberSize;
Console.WriteLine(string.Format("{0} is size {1}",
_numberName, _numberSize));
}
#endregion
}
// ... Four, Five and so on
#endregion
The example include 3 parts - the flyweight factory (NumberFactory), the flyweight
itself (Number) and flyweight concretes (One, Two, Three). You can see that lazy loading
is used in the factory. Whenever a number is needed and isn't in the factory I build it and
then return it in the GetNumber method.
Summary
To sum up the post, use the flyweight when you have a large amount of objects that
consume large amount of memory. Also use the pattern when you have groups of
objects that share common state.
From my experience, the pattern is rarely used. Even though, you should know the
pattern in order to use it when it is needed.
In the next posts I'm going to start explaining about the creational patterns.
Bridge Pattern
In this post I'm going to explain the use of the bridge pattern.
You can read my previous posts on structural patterns here:
Decorator pattern
Proxy pattern
Facade pattern
Adapter pattern
Composite pattern
The Bridge Pattern
The bridge pattern decouples an abstraction from it's implementation so the
two can vary independently. In other words we make a bridge between
the abstraction and it's implementation and therefore we won't have a
binding between the two. The pattern helps us whenever we need to
select or switch the implementation at runtime.
For a UML diagram of the pattern go to dofactory site.
Example in C#
How does it work? Lets look at a small example.
#region The Abstarction
public class Abstraction
{
#region Members
private Bridge _bridge;
#endregion
#region Ctor
/// <summary>
/// Construct a new Abstraction object with
/// the given bridge
/// </summary>
/// <param name="bridge">The given bridge</param>
public Abstraction(Bridge bridge)
{
_bridge = bridge;
}
#endregion
#region Methods
/// <summary>
/// The method demonstrate the call for
/// the bridge object by its abstraction
/// </summary>
public void Operaion()
{
Console.Write("Using");
_bridge.OperationImplementation();
}
#endregion
}
#endregion
#region The Bridge And Its Implementations
public interface Bridge
{
void OperationImplementation();
}
public class BridgeImplementationA : Bridge
{
#region Bridge Members
/// <summary>
/// Perform implementation A operation
/// </summary>
public void OperationImplementation()
{
Console.Write("BridgeImplementationA");
}
#endregion
}
public class BridgeImplementationB : Bridge
{
#region Bridge Members
/// <summary>
/// Perform implementation B operation
/// </summary>
public void OperationImplementation()
{
Console.Write("BridgeImplementationB");
}
#endregion
}
#endregion
You can see that the abstraction holds a bridge object and gets the bridge in the
constructor. Therefore, whenever one of the implementation is needed you pass it
in the constructor and you won't be coupled to the implementation.
Summary
To sum up the post, use the bridge pattern whenever you identify that the operations
you write not always need to be implemented in the same way.
Another reason is not to bind the abstraction to its implementation.
In the next post I'll cover the last pattern in the structural patterns -
the flyweight pattern.
Composite Pattern
Today it's the turn of composite pattern to be revealed.
You can read my previous posts on structural patterns here:
Decorator pattern
Proxy pattern
Facade pattern
Adapter pattern
What is Composite Pattern?
The composite pattern is enabling us to treat single component or a composition (group) of
components in the same way. There are typical operations that can be performed on these
components which include operations like add, remove, get item and so on.
A simple example is the activity model of windows workflow foundation.
Activities are the building blocks of workflow. Every activity is easy to create, either
from writing code or by composing it from other activities. Therefore you can use activities
on their own or you can make activities that include activities inside.
The activities inherit from CompositeActivity which gives them the composite abilities.
For a UML diagram of the pattern go to dofactory site.
C# Example
Lets look at an example of composite pattern.
#region Interface For The Composite Pattern
public interface IComponent
{
#region Properties
string Name
{
get;
set;
}
#endregion
#region Methods
void Add(IComponent component);
void Remove(IComponent component);
void Display(int depth);
#endregion
}
#endregion
#region Compoite Leaf
public class Component : IComponent
{
#region Ctor
/// <summary>
/// Consruct a new Component object with the
/// given name
/// </summary>
/// <param name="name">The given names</param>
public Component(string name)
{
Name = name;
}
#endregion
#region IComponent Members
/// <summary>
/// The name of the component
/// </summary>
public string Name
{
get; set;
}
/// <summary>
/// This method isn't implemented because a component
/// is a leaf of the composite
/// </summary>
public void Add(IComponent component)
{
throw new NotImplementedException("Can't add component");
}
/// <summary>
/// This method isn't implemented because a component
/// is a leaf of the composite
/// </summary>
public void Remove(IComponent component)
{
throw new NotImplementedException("Can't remove component");
}
/// <summary>
/// Displays the depth of the component
/// </summary>
/// <param name="depth">The depth of the component</param>
public void Display(int depth)
{
Console.WriteLine(
string.Format("The {0} is in depth {1}", Name, depth));
}
#endregion
}
#endregion
#region Composite
public class Composite : IComponent
{
#region Members
private List<IComponent> children;
#endregion
#region Ctor
/// <summary>
/// Construct a new Composite object with the give name
/// </summary>
/// <param name="name">The given name</param>
public Composite(string name)
{
Name = name;
children = new List<IComponent>();
}
#endregion
#region IComponent Members
/// <summary>
/// The name of the composite
/// </summary>
public string Name
{
get; set;
}
/// <summary>
/// Adds the given component to the composite
/// </summary>
/// <param name="component">The component to add</param>
public void Add(IComponent component)
{
children.Add(component);
}
/// <summary>
/// Removes the given component form the composite
/// </summary>
/// <param name="component">The component to remove</param>
public void Remove(IComponent component)
{
children.Remove(component);
}
/// <summary>
/// Display the depth of the composite and it's
/// children
/// </summary>
/// <param name="depth">The given depth to display</param>
public void Display(int depth)
{
Console.WriteLine(
string.Format("The {0} is in depth {1}", Name, depth));
foreach (IComponent component in children)
{
component.Display(depth + 1);
}
}
#endregion
}
#endregion
We have 3 players in the example - the IComponent, the Component and the Composite.
The IComponent is the API of the Composite and the Component.
The Component is the leaf of the composed tree of Components.
The Composite is the object to compose IComponent objects.
You can build a hierarchal tree of components using the Composite object.
Summary
Lets sum up the post. The composite pattern is widely used.
You should use the composite pattern whenever you encounter an hierarchical structure
of objects and composite of these objects.
In the next post in this series I'm going to write about the bridge pattern.