How to use code from one class in another C# [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am trying to link a random seating code to my User Interface class and I am stumped on how I could call upon this at anytime through my user interface. The below is currently kept inside a FirstClassCarriage class.
{
Random rand = new Random();
bool[] seats = new bool[32];
//To keep a separate list of seats taken
List<int> seatsBooked = new List<int>();
bool quit = false;
do
{
Console.Clear();
//int seatAssignFirstClass = rand.Next(0, 32);-> Moved to switch-case 1: block
int seatAssignThirdClass = rand.Next(32);
int seatAssignFirstClass; //Variable moved from main loop
//Are there any seats booked already or this is the first?
if (seatsBooked.Count == 0) //if this is the first seat to be booked...
{
seatAssignFirstClass = rand.Next(0, 32);
seats[seatAssignFirstClass] = true;
seatsBooked.Add(seatAssignFirstClass); //Add seat to the list of booked seats.
}
else
{
do //while there are available seats and current seat has not being assigned before.
{
seatAssignFirstClass = rand.Next(0, 32);
if (!seatsBooked.Contains(seatAssignFirstClass)) //if seatAssignFirstClass is not booked.
{
seats[seatAssignFirstClass] = true;
}
//repeat while the random seat number is already booked and there are avaialable seats
} while (seatsBooked.Contains(seatAssignFirstClass) && seatsBooked.Count < 32);
//IMPORTANT: Number on line bellow needs tos be one bigger than rest
if (seatsBooked.Count < 34) //if seatsBooked list is not full for First Class
{
seatsBooked.Add(seatAssignFirstClass); //Add current random-generated seat to the list.
}
}
//IMPORTANT: Number on line bellow needs tos be one bigger than rest
if (seatsBooked.Count >= 34) //If all seats are booked
{
Console.WriteLine("All seats for First Class are booked");
Console.WriteLine("Press enter to continue...");
}
else //Give customer their seat nmumber
{
Console.WriteLine("Train seat number: {0}", seatAssignFirstClass + 1);
Console.WriteLine("Press enter to continue to the main menu...");
}
} while (!quit);

The smallest unit of code reuse in C# is a function. You need to put this code in a function, and then you can call it from other places. Easiest would be for that to be a public static function, but as you learn more about design you may find that there are better ways of sharing the functionality.

You can add a reference to classes in other files with the using keyword and the desired class' namespace
Example:
namespace MyProject.MyCore {
public class MyClass {
public void MyMethod() { }
}
}
You would then refer to this namespace in the calling class, like so:
using MyProject.MyCore
Which allows you to instantiate the class object, like so:
var myInstantiatedClass = new MyClass();
And call it's method like so:
myInstantiatedClass.MyMethod();
Methods can also be marked static, which removes the need to instantiate the class, and would be called instead using the Type.Method() syntax, like MyClass.MyMethod().
You can also forgo adding a reference by using a fully qualified path.
var myInstantiatedClass = new MyProject.MyCore.MyClass()
Of course, if this code is in a different project, or assembly, you'll have to add a reference to the project or binary to gain access to the types it provides.

This example may be your answer
Or you can use static methods like
namespace MyProject.MyCore {
public class MyClass {
public static void MyMethod() { }
}
}
And you can use this method from another class
MyProject.MyCore.MyClass.MyMethod();

Related

List resetting when adding new items (Edited title to reflect actual issue)

Original Title: Get-Set to add Object w/multiple properties into a list C#
Edit: I had originally thought the issue was in setting up properties for the list objects, when it was an issue with regards to where I had initialized the list in my main code class.
Original Post:
New to coding, taking a C# course. We're working on encapsulation and get:set/properties.
The assignment says that we have to build a class that creates a die with an input number of sides, and "roll" the die for a random number. Easy!
In a second class, we have to build a function to add or remove any number of dice to the pool, and then roll them all for a result.
I'm assuming they want the dice pool to be a list that is private.
My logic going in was to create the single OneDie class, and then using a xDy notation in the main program prompt to add x number of die with y sides to the list. (ie: add 2d6)
I've built an AddDie function that should do that, but when I check my list count after it's done, the count is 0. The private list (_dicePool) seems to be re-setting to zero every time I try to add a new object to the list. I suspect I'm not building my property DicePool's get/set functionality correctly, but I'm not sure how to call my 2-parameter AddDice function from inside the DicePool{set}, or even if that's the approach I should take.
Assuming the list should be private, am I missing something to permanently add new objects to the list?
Edit to add: OR, would it be better to create a ManyDice object? But how do I build this.Sides and this.Roll from the OneDie object?
Here's my code that's applicable to adding objects (dice) to the list (dicepool).
class ManyDice
{
private List<OneDie> _dicePool = new List<OneDie>();
//What I think I might have to do:
public List<OneDie> DicePool
{
get
{
return this._dicePool;
}
set
{
//???????????? how do I call AddDice, when I need 2 parameters for it?
}
}
public void AddDie(int number, int sides)
{
for (int i = 0; i < number; i++)
{
this.dicePool.Add(new OneDie(sides));
}
}
}
class OneDie
{
private int _sides, _rolledValue;
public int Sides
{
get
{
return this._sides;
}
set
{
this._sides = value;
}
}
public int RollValue
{
get
{
return this._rolledValue;
}
set
{
this._rolledValue = value;
RollIt(value);
}
}
public OneDie()
{
}
public OneDie(int sides)
{
this.Sides = sides;
this.RollValue = sides;
}
private int RollIt (int sides)
{
Random random = new Random((int)DateTime.Now.Ticks);
return random.Next(1, (sides + 1));
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Let's roll some dice!");
Console.WriteLine("Please enter the number of dice you want to roll in the following format:");
Console.WriteLine("xdy, where \"x\" is the number of dice you want and \"y\" is how many sides they have.");
Console.WriteLine("Example: 2d6 is 2 6-sided dice. Perfect for playing Catan! (or Monopoly)");
Console.WriteLine("Please limit your dice to d4, d6, d8, d10, d12, d20");
Console.WriteLine("To add a die, type \"add xdy\" to add x number of y sided dice.");
Console.WriteLine("To remove a die, type \"remove xdy.\" to remove x number of y sided dice.");
Console.WriteLine("Type \"dice\" to see a list of all the dice in the pile.");
Console.WriteLine("Type \"roll\" to roll all the dice and see the results!");
Console.WriteLine("Type \"clear\" to clear all the dice and start agin!");
Console.WriteLine("Type \"exit\" to exit program\n");
PlayDice();
Console.ReadKey();
}
static void PlayDice()
{
do
{
string[] xDy = null;
int numberOfDice = 1;
int numberOfSides=1;
Console.WriteLine("\nEnter your command:");
string option = Console.ReadLine();
option = option.ToLower().Trim();
string[] words = option.Split(' ');
string command = words[0];
//CheckCommand(command);
if (words.Length > 1)
{
xDy = words[1].Split('d');
numberOfDice = int.Parse(xDy[0]);
numberOfSides = int.Parse(xDy[1]);
}
ManyDice die = new ManyDice();
if (command == "exit")
{
Console.WriteLine("Thank you, play again, soon!");
break;
}
else if (command == "add")
{
//numberOfSides=CheckDice(numberOfSides);
die.AddDie(numberOfDice, numberOfSides);
Console.WriteLine("You have {0}ed {1} {2}-sided dice.", command, numberOfDice, numberOfSides);
}
else if (command == "remove")
{
Console.WriteLine("You have {0}d {1} {2}-sided dice.", command, numberOfDice, numberOfSides);
}
else if (command == "dice")
{
Console.WriteLine("These are your dice:");
die.Display();
}
else if (command == "roll")
{
Console.WriteLine("Here is your roll:");
}
else if (command == "clear")
{
Console.WriteLine("All dice have been cleared.");
}
} while (true);
}
static int CheckDice(int sides)
{
List<int> check = new List<int> {4,6,8,10,12,20};
while (!check.Contains(sides))
{
Console.WriteLine("{0}-sided dice are not available.\nPlease enter 4,6,8,10,12 or 20");
sides = int.Parse(Console.ReadLine());
}
return sides;
}
static string CheckCommand(string instructions)
{
List<string> check = new List<string> { "add", "remove", "dice", "roll","clear", "exit" };
while (!check.Contains(instructions))
{
Console.WriteLine("Command not recognized.\nPlease enter \"add\", \"remove\", \"dice\", \"roll\",\"clear\", or \"exit\"");
instructions = Console.ReadLine();
}
return instructions;
}
}
New Answer based on comments and updated question:
The line ManyDice die = new ManyDice(); is wiping your dice list clean every loop through your program. It's replacing your variable with a new instance of the class, with a fresh list and all.
Simply move that line before the start of the loop:
before the line do {
and then every iteration will use the same instance of ManyDice, and will all share the variable die, without overwriting it.
OLD ANSWER: From what I can see, your program only runs once. And then you need to start it again to put in another dice. Your main function only asks for input once. Whenever you start the program again, all the memory used in the program gets cleared. Unless I’m missing something, that is why your list continues to be reset. You’re actually running a completely new program the next time you try to add dice. So it has no knowledge of the previous runs.
One solution is to say (pseudo code)
While (running) {
// what you have now
if (option == “done”) running = false;
if (option == “roll”) // roll all dice.
}
This will keep prompting the user for commands until they type done. And it remains the same program so that you don’t lose the data from earlier commands.
Update based on comment: you’re recreating the ManyDice instance on each iteration, effectively starting from scratch. Save the instance outside the while loop and then reuse it.
Hint:
Your rolling should probably be done by manyDice.RollAll() And maybe should return a List of RollResults.

I cannot solve this simple exercise in Deitel Visual C# 2010 [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I've been studying Deitel's Visual C# 2010. I'm stuck at an exercise in the chapter about arrays. It's been bugging me for days now, and I've written the code several times now and each times something goes wrong.
The exercise is asking to develop a new reservation system for a small airline company. The capacity of an airplane is 10 seats. So you ask the customer to input 1 for First Class and 2 for Economy. Seats from 1 to 5 are for first class. Seats from 6 to 10 are for economy.
I must use a one-dimensional array of type bool to represent the seating char of the plane. Initialize all elements of the array to false to represent vacant seats (Luckily bool initializes to false anyway, because I do not know how to initialize an array). As each seat is assigned, set the corresponding element in the plane to true.
The app should never assign a seat that's already been seated. When the economy class is full, the app should ask the customer if he wants to fly in first class (and vice versa). If the customer responds with yes, assign him in the economy class (if there's an empty seat there). If there is no empty seats in economy or the customer refuses to fly in first class, then just display to him that "The next flight is after three hours!).
I'm self-studying the book. This is not an assignment or homework. I really do not wish to post the code I've written because I want a completely fresh way to solve the problem, but I'm pretty sure that I will be asked about the code, so here it is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Craps_Game
{
class AirlineReservation
{
private static bool[] seats = new bool[10];
private static void FirstClass(ref bool[] seats)
{
for(int i = 0; i < 5; i++)
{
if (seats[i] == false)
{
seats[i] = true;
Console.WriteLine("Your seat number is " + i);
break;
}
else
{
Console.WriteLine("First Class is full. Would you like to fly Economy?");
if (Console.ReadLine().ToLower() == "y")
Economy(ref seats);
else
Console.WriteLine("The next flight is in three hours!. Good bye");
}
}
}
private static void Economy(ref bool[] seats)
{
for (int i = 5; i < 10; i++)
if (seats[i] == false)
seats[i] = true;
else
{
Console.WriteLine("Economy class is full. Would you like to fly First Class");
if (Console.ReadLine().ToLower() == "y")
FirstClass(ref seats);
else
Console.WriteLine("The next flight is in three hours!. Good Bye");
}
}
public static void Reservation()
{
do
{
Console.WriteLine("Enter 1 to fly First Class");
Console.WriteLine("Enter 2 to fly Economy Class");
if (Convert.ToInt32(Console.ReadLine()) == 1)
FirstClass(ref seats);
else
Economy(ref seats);
} while (true);
}
}
}
Bear in mind that I would prefer a completely different way of solving the problem, instead of fixing this one :)
I don't want to solve the entire problem in code since you are learning, but I will show you the major things I saw in your code that is a bug.
Your Economy method was not exiting the loop if it found that a
seat was available. It needed to break.
Your else when you did not find a seat was not checking if all of the seats were taken. Checking if i == 9 will make it so it will only go to FirstClass if there are no more seats, not before.
private static void Economy(ref bool[] seats)
{
for (int i = 5; i < 10; i++)
{ // <---- Added the brackets
if (seats[i] == false)
{
seats[i] = true;
break; // <----- You forgot to break when you reserved a seat.
}
else if (i == 9) // <---- You weren't checking if all seats are taken.
{
Console.WriteLine("Economy class is full. Would you like to fly First Class");
if (Console.ReadLine().ToLower() == "y")
FirstClass(ref seats);
else
Console.WriteLine("The next flight is in three hours!. Good Bye");
}
}
}
I like the way you approach programming and your task and this site!
Therefore I would hate to write out the code for you - all the fun is getting it done by yourself. But since you are stuck let me give you a few hints:
Starting at the bottom, the first thing that comes to mind is that you are always offering both classes even when one or both are full. Here is a function header that could help to break things down even further than you already have done:
public int getFirstVacantSeatIn(int classType)
// returns 1-5 for classType=1, 6-10 for classType=2, -1 if classType is full
You can use this function to make the prompt dynamic like this:
Console.WriteLine( ( getFirstVacantSeatIn(1) >= 0 ?
"Enter 1 to fly First Class") : "First Class is full");
And you can reuse it when you try to assign the new seats..:
Another point is that you offer switching between classes when one is full without checking if the other one actually isn't full, too. I suspect that is the problem you are facing?
So you should check before offering to up- or downgrade.. The above function will help here as well. If you can re-use something, chances are that it was right to create that thing..
The secret is ever so often to break your problem down further and further until it goes away, always using/creating useful names for the sub-problems..
"I must use a one-dimensional array of type bool to represent the seating char of the plane"
"I want a completely fresh way to solve the problem"
"Bear in mind that I would prefer a completely different way of solving the problem, instead of fixing this one"
So be it! Others have given you really good advice already, but here's "fresh" way to do it.
using System;
namespace FunnyConsoleApplication
{
public class Program
{
static void Main(string[] args)
{
Airplane plane = new Airplane();
bool reserve = true;
while (reserve)
{
Console.Clear();
Console.WriteLine("Enter [1] to fly First Class ({0} vacant)", plane.VacantFirstClassSeats());
Console.WriteLine("Enter [2] to fly Economy Class ({0} vacant)", plane.VacantEconomySeats());
string input = Console.ReadLine();
switch (input)
{
case "1":
if (plane.HasFirstClassSeats)
{
plane.ReserveFirstClassSeat();
}
else if (plane.HasEconomySeats)
{
if (IsOk("No vacancy, enter [y] to fly Economy instead?"))
plane.ReserveEconomySeat();
else
reserve = false;
}
else
{
reserve = false;
}
break;
case "2":
if (plane.HasEconomySeats)
{
plane.ReserveEconomySeat();
}
else if (plane.HasFirstClassSeats)
{
if (IsOk("No vacancy, enter [y] to fly First Class instead?"))
plane.ReserveFirstClassSeat();
else
reserve = false;
}
else
{
reserve = false;
}
break;
}
Console.WriteLine();
}
Console.WriteLine("No can do, good bye!");
Console.ReadLine();
}
private static bool IsOk(string question)
{
Console.WriteLine(question);
return string.Compare(Console.ReadLine(), "y", StringComparison.OrdinalIgnoreCase) == 0;
}
}
public class Airplane
{
private readonly bool[] _seats = new bool[10];
public bool HasFirstClassSeats
{
get { return HasSeats(0); }
}
public bool HasEconomySeats
{
get { return HasSeats(5); }
}
public int VacantFirstClassSeats()
{
return GetVacant(0);
}
public int VacantEconomySeats()
{
return GetVacant(5);
}
public void ReserveFirstClassSeat()
{
Reserve(0);
}
public void ReserveEconomySeat()
{
Reserve(5);
}
private bool HasSeats(int index)
{
for (int i = index; i < index + 5; i++)
{
if (!_seats[i])
return true;
}
return false;
}
private int GetVacant(int index)
{
int count = 0;
for (int i = index; i < index + 5; i++)
{
if (!_seats[i])
count++;
}
return count;
}
private void Reserve(int index)
{
for (int i = index; i < index + 5; i++)
{
if (!_seats[i])
{
_seats[i] = true;
break;
}
}
}
}
}
Which gives you

C# how to make a pin code, that only unlocks something when typed in the right order [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I am making a vault application that requires a code to be entered in a specific order (1 2 3)
Now I have made that the vault will unlock when 1 2 and 3 are pressed, but the order doesn't matter at all.
I want to make that the vault ONLY unlocks when the button 1 is pressed first, 2 second and 3 thirth.
Thanks!
You might be overthinking this.
public bool VaultLock(string key)
{
if(key == "1234")
return true;
return false;
}
Append the numbers to a string and then match the string to the correct PIN.
private static string THECORRECTPIN = "1234";
private string _PinEntered{get;set;}
public void KeyPressed(string number)
{
_PinEntered += number;//here's the important bit
if(_PinEntered == THECORRECTPIN)
{
Unlock();
}
}
Make a statemachine that accepts an input (one letter) causing it to move to another state. 2.
States can be valid (all the input is valid), invalid (more input is needed) or error (valid state can never be reached again).
Give it five states:
1. Start, no input has been given, with a transition on input 1 to state 2.
2. Has a transition on input 2 to state 3.
3. Has a transition on input 3 to state 4.
4. Is a valid state, has no transitions
5. Error state, no transitions.
If an input is given that the current state has no stransitions for go to the error state.
Update, this concept can get very complicated, it forms the basics for search engines. This implementation is extremely basic:
namespace Algorithms
{
public class Automaat
{
public const char Epsilon = default(char);
private Graph current, valid;
public Automaat(string text)
{
Graph start = new Graph();
valid = new Graph();
current = start;
foreach (var letter in text)
{
var next = new Graph();
current.SetTransition(next, letter);
current = next;
}
valid = current;
current = start;
}
public void Input(char symbol)
{
if (current != null)
current = current.Next(symbol);
}
public bool IsValid()
{
return current == valid;
}
/// <summary>
/// Holds connections to other graphs, the position of that graph in the array relative to the alphabet determines what symbol that link uses.
/// </summary>
private class Graph
{
private static char[] alphabet;
Graph[] nodes;
public Graph()
{
nodes = new Graph[26];
}
public void SetTransition(Graph graph, char symbol)
{
nodes[ConvertToIndex(symbol)] = graph;
}
public Graph Next(char symbol)
{
return nodes[ConvertToIndex(symbol)];
}
private int ConvertToIndex(char symbol)
{
return symbol - 'a';
}
}
}
}

Updating ListBox with new list values through using a method

I am trying to prompt a method to start upon a button click. The problem is that they are over two different classes and it is difficult to share all the relevant values over both the form and the class. I was wondering if it was possible to change a listbox's values through pressing a button on a form. At the moment mine is just staying on the first list.
My first class is the form. This is holding the physical changing of the data.
if (teach.Alphabet == "B")
{
lbl1.Text = "What is the english word for White?";
lstNum.Items.AddRange(number1);
txtQuestion.Text = String.Join(Environment.NewLine, english1);
}
It calls the method teach.CallRandomQuestion() to get the alphabet letter (it is a way it chooses which question to put onto the system). This is done by the following code:
public void CallRandomQuestion()
{
if (score == 1)
{
Alphabet = "A";
Answer = "1";
}
if (score == 2)
{
Alphabet = "B";
Answer = "3";
}
}
Finally I have a public int score which starts at 1 and adds one every time the correct answer is given. So if the correct answer is given, the second question B should show in the listbox, but doesn't.
public void Showinlistbox()
{
// Add a listBox named listBox1
// Add Score
score++;
CallRandomQuestion();
//if you want mutual-exclusion lock add lock (listBox1)
listBox1.Items.Add(Alphabet);
}

Tiered Design With Analytical Widgets - Is This Code Smell? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
The idea I'm playing with right now is having a multi-leveled "tier" system of analytical objects which perform a certain computation on a common object and then create a new set of analytical objects depending on their outcome. The newly created analytical objects will then get their own turn to run and optionally create more analytical objects, and so on and so on. The point being that the child analytical objects will always execute after the objects that created them, which is relatively important. The whole apparatus will be called by a single thread so I'm not concerned with thread safety at the moment. As long as a certain base condition is met, I don't see this being an unstable design but I'm still a little bit queasy about it.
Is this some serious code smell or should I go ahead and implement it this way? Is there a better way?
Here is a sample implementation:
namespace WidgetTier
{
public class Widget
{
private string _name;
public string Name
{
get { return _name; }
}
private TierManager _tm;
private static readonly Random random = new Random();
static Widget()
{
}
public Widget(string name, TierManager tm)
{
_name = name;
_tm = tm;
}
public void DoMyThing()
{
if (random.Next(1000) > 1)
{
_tm.Add();
}
}
}
//NOT thread-safe!
public class TierManager
{
private Dictionary<int, List<Widget>> _tiers;
private int _tierCount = 0;
private int _currentTier = -1;
private int _childCount = 0;
public TierManager()
{
_tiers = new Dictionary<int, List<Widget>>();
}
public void Add()
{
if (_currentTier + 1 >= _tierCount)
{
_tierCount++;
_tiers.Add(_currentTier + 1, new List<Widget>());
}
_tiers[_currentTier + 1].Add(new Widget(string.Format("({0})", _childCount), this));
_childCount++;
}
//Dangerous?
public void Sweep()
{
_currentTier = 0;
while (_currentTier < _tierCount) //_tierCount will start at 1 but keep increasing because child objects will keep adding more tiers.
{
foreach (Widget w in _tiers[_currentTier])
{
w.DoMyThing();
}
_currentTier++;
}
}
public void PrintAll()
{
for (int t = 0; t < _tierCount; t++)
{
Console.Write("Tier #{0}: ", t);
foreach (Widget w in _tiers[t])
{
Console.Write(w.Name + " ");
}
Console.WriteLine();
}
}
}
class Program
{
static void Main(string[] args)
{
TierManager tm = new TierManager();
for (int c = 0; c < 10; c++)
{
tm.Add(); //create base widgets;
}
tm.Sweep();
tm.PrintAll();
Console.ReadLine();
}
}
}
Yes, I call the following code smell:
_currentTier = 0;
while (_currentTier < _tierCount) //_tierCount will start at 1 but keep increasing because child objects will keep adding more tiers.
{
foreach (Widget w in _tiers[_currentTier])
{
w.DoMyThing();
}
_currentTier++;
}
You are iterating over a collection as it is changing. I mean the first iteration, not the second. You're obviously accounting for the change (hence the < _tierCount rather than a standard foreach) but it's still a smell, IMO.
Would I let it into production code? Possibly. Depends on the scenario. But I'd feel dirty about it.
Also: Your _tiers member could just as easily be a List<List<Widget>>.
The biggest potential issue here is that the Sweep method is iterating over a collection (_tiers) that could potentially change in a call to Widget.DoMyThing().
The .NET BCL classes do not allow collection to change while they are being iterated. The code the way it's structured, exposes a risk that this may happen.
Beyond that, the other problem is that the structure of the program makes it difficult to understand what happens in what order. Perhaps you could separate the phase of the program that recursively assembles the model from the portion that visits the model and performs computations.
+1 to both Randolpho and LBushkin.
However, I gave it some thought and I think I know why this smells. The pattern as I have implemented seems to be some kind of perversion of the Builder pattern. What would work better is creating a Composite out of a series of analytical steps which, taken as a whole, represents some kind of meaningful state. Each step of the analytical process (behavior) should be distinct from the output Composite (state). What I've implemented above meshes both the state and the behavior together. Since the state-holders and state-analyzers are one and the same object, this also violates the Single Responsibility Principle. The approach of having a composite "build itself" opens up the possibility of creating a vicious loop, even though my prototype above has a deterministic completion.
Links:
Builder Pattern
Composite Pattern

Categories

Resources