Resumo : neste tutorial, você aprenderá como usar o padrão C# Composite para tratar objetos individuais e composições de objetos de maneira uniforme.
Introdução ao padrão composto C#
O padrão C# Composite é um padrão de design estrutural que permite criar uma hierarquia de objetos individuais e coleções de objetos por meio de uma interface compartilhada . Isso permite que o cliente trate objetos individuais e o grupo de objetos de maneira uniforme.
Como um objeto individual e uma coleção de objetos compartilham uma interface comum , o cliente não precisa saber onde está gerenciando um único objeto ou um objeto de coleção.
Em vez disso, o cliente pode fazer uso do polimorfismo sem ter que aplicar instruções de condição desnecessárias que verificam se o objeto é um objeto individual ou uma coleção de objetos.
Para ilustrar esse padrão composto, imagine que você precisa gerenciar uma lista de materiais (BOM) que contém uma lista de materiais para fazer uma parte de um produto (montagem) ou o produto inteiro.
Em alguns casos, é necessário executar a mesma operação em um componente, seja ele um componente único ou uma montagem que consiste em componentes individuais.
Por exemplo, você deve conseguir obter o custo de um componente sem saber se é um componente ou uma montagem.
O diagrama UML a seguir ilustra o padrão composto:
Neste diagrama:
IComponent
: esta é uma interface compartilhada que especifica a operação que cada componente precisa implementar.Leaf
: Este é um componente individual.Composite
: Esta é uma coleção de componentes. Pode incluir folhas e outros compostos.Client
: Esta classe tem acesso a diferentes componentes através da interface compartilhadaIComponent
, aproveitando o polimorfismo.
Exemplo de padrão composto C#
O seguinte programa C# demonstra o padrão composto:
// Shared interface
public interface IComponent
{
string Name { get; set; }
int Quantity { get; set; }
double GetCost();
}
// Leaf component
public class Part : IComponent
{
public string Name { get; set; }
public int Quantity { get; set; }
public double Cost { get; set; }
public Part(string name, int quantity, double cost)
{
Name = name;
Quantity = quantity;
Cost = cost;
}
public double GetCost() => Cost * Quantity;
}
// Composite component
public class Assembly : IComponent
{
public string Name { get; set; }
public int Quantity { get; set; }
private readonly List<IComponent> _components = new();
public Assembly(string name, int quantity, IEnumerable<IComponent> components)
{
Name = name;
Quantity = quantity;
_components.AddRange(components);
}
public double GetCost() => _components.Sum(component => component.GetCost());
}
// Client
public class Program
{
public static void Main(string[] args)
{
// parts
var engine = new Part("Engine", 1, 5000.0);
var tires = new Part("Tires", 4, 1000.0);
// assembly
var body = new Assembly(
"Body",
1,
new List<IComponent> {
new Part("Frame", 1, 2000.0),
new Part("Doors", 4, 1000.0),
new Part("Windows", 6, 500.0)
}
);
// car
var car = new Assembly(
"Car",
1,
new List<IComponent> {
engine,
tires,
body
});
// Calculate the cost of the car
var carCost = car.GetCost();
Console.WriteLine($"The cost of the car is: {carCost:C}");
}
}
Linguagem de código: C# ( cs )
Saída:
The cost of the car is: $18,000.00
Linguagem de código: C# ( cs )
Como funciona.
Primeiro, defina uma interface compartilhada
que tanto o componente quanto o assembly precisam implementar. A IComponent
interface possui as propriedades IComponent
Name
e Quantity
, bem como o GetCost()
método:
public interface IComponent
{
string Name { get; set; }
int Quantity { get; set;}
double GetCost();
}
Linguagem de código: C# ( cs )
Segundo, defina a Part
classe que serve como folha. A Part
classe implementa a IComponent
interface:
public class Part : IComponent
{
public string Name { get; set; }
public int Quantity { get; set; }
public double Cost { get; set; }
public Part(string name, int quantity, double cost)
{
Name = name;
Quantity = quantity;
Cost = cost;
}
public double GetCost() => Cost * Quantity;
}
Linguagem de código: C# ( cs )
Na classe Peça, GetCost()
calcula o custo multiplicando o custo de cada componente pela quantidade.
Terceiro, defina a Assembly
classe que serve como composta. A Assembly
classe implementa a
interface e possui um campo do tipo IComponent
IEnumerable
<
> que representa uma lista de IComponent
:IComponent
public class Assembly : IComponent
{
public string Name { get; set; }
public int Quantity { get; set; }
private readonly List<IComponent> _components = new();
public Assembly(string name, int quantity, IEnumerable<IComponent> components)
{
Name = name;
Quantity = quantity;
_components.AddRange(components);
}
public double GetCost() => _components.Sum(component => component.GetCost());
}
Linguagem de código: C# ( cs )
O GetCost()
método calcula o custo total usando o Sum
método de extensão somando os custos de todos os componentes da _component
lista.
Por fim, defina a Program
classe que usa as classes Part
e Assembly
para construir um produto Carro e chame o GetCost()
objeto carro para obter o custo total do carro:
public class Program
{
public static void Main(string[] args)
{
// parts
var engine = new Part("Engine", 1, 5000.0);
var tires = new Part("Tires", 4, 1000.0);
// assembly
var body = new Assembly("Body", 1, new List<IComponent> {
new Part("Frame", 1, 2000.0),
new Part("Doors", 4, 1000.0),
new Part("Windows", 6, 500.0)
});
// car
var car = new Assembly("Car", 1, new List<IComponent>{
engine,
tires,
body
});
// Calculate the cost of the car
var carCost = car.GetCost();
Console.WriteLine($"The cost of the car is: {carCost:C}");
}
}
Linguagem de código: C# ( cs )
Resumo
- Use o padrão Composite para tratar objetos individuais e composições de objetos de maneira uniforme.