Что-то вроде:
namespace Beverages
{
public class Builder
{
private static Tuple<Condiments, Condiments>[] notAllowedTogether = new[]
{
new Tuple<Condiments,Condiments>(Condiments.Soy,Condiments.Mocha)
};
public static Beverage Build(Beverages beverage, params Condiments[] condiments)
{
// check if theres a combination that is not allowed
if (notAllowedTogether.Any(na => condiments.Contains(na.Item1) && condiments.Contains(na.Item2)))
{
return null; // or throw exception or anything
}
// creation of the base beverage using reflection "Beverages." is the namespace the classes are defined in
Beverage result = (Beverage)Activator
.CreateInstance(
Type.GetType("Beverages." + beverage.ToString()));
// adding the condiments by calling the constructors of the classes
foreach (Condiments condiment in condiments)
{
result = (Beverage)Type
.GetType("Beverages." + condiment.ToString())
.GetConstructor(new Type[]{typeof(Beverage)})
.Invoke(new object[]{result});
}
return result;
}
}
// in order to decouple the classes from the client
// i have introduced enums containing the classes names
public enum Condiments
{
CoffeeMate,
Soy,
Mocha
}
public enum Beverages
{
Expresso,
HouseBlend,
DarkRoast
}
public abstract class Beverage
{
private string description = "Unknown Beverage";
public abstract double Cost();
public virtual string Description
{
get { return description; }
}
}
public abstract class CondimentDecorator : Beverage
{
public abstract override string Description { get; }
}
class Expresso : Beverage
{
public override double Cost()
{
return 1.99;
}
public override string Description
{
get { return "Expresso"; }
}
}
class HouseBlend : Beverage
{
public override double Cost()
{
return .89;
}
public override string Description
{
get { return "House Blend Coffee"; }
}
}
class DarkRoast : Beverage
{
public override double Cost()
{
return .99;
}
public override string Description
{
get { return "Dark Roast Coffee"; }
}
}
class Mocha : CondimentDecorator
{
private Beverage beverage;
public Mocha(Beverage beverage)
{
this.beverage = beverage;
}
public override double Cost()
{
return (.20 + beverage.Cost());
}
public override string Description
{
get { return beverage.Description + ", Mocha"; }
}
}
class Soy : CondimentDecorator
{
private Beverage beverage;
public Soy(Beverage beverage)
{
this.beverage = beverage;
}
public override double Cost()
{
return (.30 + beverage.Cost());
}
public override string Description
{
get { return beverage.Description + ", Soy"; }
}
}
class CoffeeMate : CondimentDecorator
{
private Beverage beverage;
public CoffeeMate(Beverage beverage)
{
this.beverage = beverage;
}
public override double Cost()
{
return (.15 + beverage.Cost());
}
public override string Description
{
get { return beverage.Description + ", Coffee Mate"; }
}
}
}
И используется как:
class Program
{
static void Main(string[] args)
{
//Expresso
Beverage expresso = Builder.Build(Beverages.Beverages.Expresso);
Console.WriteLine(expresso.Description + " $" + expresso.Cost());
Beverage houseBlend = Builder.Build(Beverages.Beverages.HouseBlend,Condiments.Mocha, Condiments.Soy);
if (houseBlend == null)
{
Console.WriteLine("Could not make that beverage");
}
else
{
Console.WriteLine(houseBlend.Description + " $" + houseBlend.Cost());
}
Beverage darkRoast = Builder.Build(Beverages.Beverages.DarkRoast, Condiments.CoffeeMate);
Console.WriteLine(darkRoast.Description + " $" + darkRoast.Cost());
Console.ReadLine();
}
}
Выход:
Expresso $1,99
Could not make that beverage
Dark Roast Coffee, Coffee Mate $1,14
Это побудит зависимости между подтипами декоратора, и вы будете обижать другие принципы проектирования ООП ... в этом случае декоратор не был бы хорошим выбором –
Что вы могли бы do - это шаблон строителя ... строитель может украсить напитки и реализовать такой вид проверки. сделать все подтипы частными и в своем собственном пространстве имен, а строитель - внутренним/общедоступным, и вы там. –
Добавлена возможность реализации –