Question:
I have a tree class that will have the addition of trunks, branches, leaves and fruits.
I need the class to give access to certain methods only after others and that the previous ones cannot be accessed again.
Example:
public class Arvore
{
public List<Membro> Membros { get; set; }
public Arvore AdicionarTronco()
{
Membros.Add(new Tronco());
return this;
}
public Arvore AdicionarGalho()
{
Membros.Add(new Galho());
return this;
}
public Arvore AdicionarFolha()
{
Membros.Add(new Folha());
return this;
}
public Arvore AdicionarFruto()
{
Membros.Add(new Fruto());
return this;
}
public void ImprimirArvore() { ... }
}
So the problem is that, after creating the Arvore
, the only method that can be accessed is AdicionarTronco()
.
After AdicionarTronco()
, only AdicionarGalho()
can be accessed, and AdicionarTronco()
can no longer be accessed.
Finally, AdicionarFolha()
and AdicionarFruto()
can be accessed, but not the other methods.
I need to give the following example functionality for the class:
(new Arvore())
.AdicionarTronco()
.AdicionarGalho()
.AdicionarFolha()
.AdicionarFruto()
.ImprimirArvore();
For that, I thought about controlling access to methods through interfaces, and I thought:
public interface IArvore
{
ITronco AdicionarTronco();
void ImprimirArvore();
}
public interface ITronco
{
IGalho AdicionarGalho();
}
public interface IGalho
{
IGalho AdicionarFolha();
IGalho AdicionarFruto();
}
Hence, make the Arvore
class descend from the interfaces:
public class Arvore : IArvore, ITronco, IGalho
{
public List<Membro> Membros { get; set; }
public ITronco AdicionarTronco()
{
Membros.Add(new Tronco());
return this;
}
public IGalho AdicionarGalho()
{
Membros.Add(new Galho());
return this;
}
public IGalho AdicionarFolha()
{
Membros.Add(new Folha());
return this;
}
public IGalho AdicionarFruto()
{
Membros.Add(new Fruto());
return this;
}
public void ImprimirArvore() { ... }
}
But I still managed to solve a little.
I managed to solve the issue of not being able to go back to the methods, but through the Arvore
I still have access to the AddBranch AdicionarGalho()
, AdicionarFolha()
and AdicionarFruto()
methods.
Still, in the end I need access to the ImprimirArvore()
method.
How can I resolve this?
Answer:
This type of problem can be solved using the Composite Design Pattern
Composite is a software design pattern used to represent an object that is constituted by the composition of objects similar to it. In this pattern, the composite object has a set of other objects that are in the same class hierarchy to which it belongs.
Let's start by writing an abstract class that will serve as a base for all the members of the composite:
The constructor receives the name of this Member(Tree, Trunk, etc) and declares two virtual methods:
AddMember – Adds members to this Member. At the lowest levels of the composite, an Exception is made because its addition is not allowed (Leaf and Fruit)
Print – Print this Member's name
public abstract class Membro
{
protected readonly string _nome ;
protected Membro(string nome)
{
_nome = nome;
}
public virtual string Nome
{
get { return _nome; }
}
public virtual void AdicionarMembro(Membro membro)
{
throw new InvalidOperationException("Não podem ser adicionados membros ao membro " + Nome);
}
public virtual void Imprimir(int nivel)
{
Console.WriteLine(new String('-', nivel) + Nome);
}
}
We need a class for the Members that have members (Tree, Trunk and Branch). It will be an abstract class and will inherit from Member .
The class declares a list where the members that compose it will be stored. The AddMember AdicionarMembro()
and Imprimir()
methods have been rewritten, the first in order to add the new members to the list, the second in order to also print the names of the child members.
public abstract class MembroComposto : Membro
{
protected IList<Membro> _membros;
protected MembroComposto(string nome) : base(nome)
{
_membros = new List<Membro>();
}
public override void AdicionarMembro(Membro membro)
{
_membros.Add(membro);
}
public override void Imprimir(int nivel)
{
Console.WriteLine(new String('-', nivel) + Nome);
foreach (var membro in _membros)
{
membro.Imprimir(nivel + 1);
}
}
}
These two classes are the basis for implementing the Composite Design Pattern
We can now start writing the concrete classes of our composite members.
Let's start with the simple members: Leaf and Fruit
public class Folha : Membro
{
public Folha()
: base("Folha")
{
}
}
public class Fruto : Membro
{
public Fruto(string nome)
:base(nome)
{
}
public Fruto()
: base("Fruto")
{
}
}
They inherit from Member and simply pass their name to the base class.
In the case of Fruit , I added another constructor, in case you want to give the fruit a specific name (Banana for example).
It remains to write the classes of the members that are composites: Tree , Trunk and Branch
public class Arvore : MembroComposto
{
public Arvore(string nome)
:base(nome)
{
}
public Arvore() : base("Arvore")
{
}
}
public class Tronco : MembroComposto
{
public Tronco()
: base("Tronco")
{
}
}
public class Galho : MembroComposto
{
public Galho()
: base("Galho")
{
}
}
They simply inherit from CompositeMember and pass its name to the base class. Another constructor was added to the Tree class to allow giving a specific name to the tree.
Having all the classes we are going to use them to compose our composite: an Apple Tree.
static void Main(string[] args)
{
//Criar algumas folhas
Membro folha1 = new Folha();
Membro folha2 = new Folha();
Membro folha3 = new Folha();
Membro folha4 = new Folha();
//Criar algumas frutas
Membro maca1 = new Fruto("maçã 1");
Membro maca2 = new Fruto("maçã 2");
Membro maca3 = new Fruto("maçã 3");
Membro maca4 = new Fruto("maçã 4");
Membro maca5 = new Fruto("maçã 5");
//Criar dois galhos
MembroComposto galho1 = new Galho();
MembroComposto galho2 = new Galho();
//Atribuir maçãs e folhas aos galhos
galho1.AdicionarMembro(folha1);
galho1.AdicionarMembro(folha2);
galho1.AdicionarMembro(maca1);
galho1.AdicionarMembro(maca2);
galho1.AdicionarMembro(maca3);
galho2.AdicionarMembro(folha3);
galho2.AdicionarMembro(folha4);
galho2.AdicionarMembro(maca4);
galho2.AdicionarMembro(maca5);
//Criar o tronco da maceira
MembroComposto tronco = new Tronco();
//Adicionar os galhos ao tronco
tronco.AdicionarMembro(galho1);
tronco.AdicionarMembro(galho2);
//Criar a macieira
MembroComposto macieira = new Arvore("Macieira");
//Adicionar o tronco
macieira.AdicionarMembro(tronco);
//Imprimir a arvore
maciera.Imprimir(1);
Console.ReadKey();
}
Output:
-Apple tree
–Trunk
—Branch
—-Leaf
—-Leaf
—-apple 1
—-mace 2
—-mace 3
—Branch
—-Leaf
—-Leaf
—-mace 4
—-mace 5
All of this may not directly answer your question, but in my opinion it is the right approach for this type of model.