c# – Architectural problem where access to construction methods must be controlled

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.

Scroll to Top