Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I am trying to get a feel of how to design and think in an Object Oriented manner and want to get some feedback from the community on this topic. The following is an example of a chess game that I wish to design in an OO manner. This is a very broad design and my focus at this stage is just to identify who is responsible for what messages and how the objects interact each other to simulate the game. Please point out if there are elements of bad design (high coupling, bad cohesion etc.) and how to improve on them.
The Chess game has the following classes
Board
Player
Piece
Square
ChessGame
The Board is made up of squares and so Board can be made responsible for creating and managing Square objects. Each piece also is on a square so each piece also has a reference to the square it is on. (Does this make sense?). Each piece then is responsible to move itself from one square to another.
Player class holds references to all pieces he owns and is also responsible for their creation (Should player create Pieces?) . Player has a method takeTurn which in turn calls a method movePiece which belongs to the piece Class which changes the location of the piece from its current location to another location. Now I am confused on what exactly the Board class must be responsible for. I assumed it was needed to determine the current state of the game and know when the game is over. But when a piece changes it's location how should the board get updated? should it maintain a seperate array of squares on which pieces exist and that gets updates as pieces move?
Also, ChessGame intially creates the Board and player objects who in turn create squares and pieces respectively and start the simulation. Briefly, this might be what the code in ChessGame may look like
Player p1 =new Player();
Player p2 = new Player();
Board b = new Board();
while(b.isGameOver())
{
p1.takeTurn(); // calls movePiece on the Piece object
p2.takeTurn();
}
I am unclear on how the state of the board will get updated. Should piece have a reference to board? Where should be the responsibility lie? Who holds what references? Please help me with your inputs and point out problems in this design. I am deliberately not focusing on any algorithms or further details of game play as I am only interested in the design aspect. I hope this community can provide valuable insights.
I actually just wrote a full C# implementation of a chess board, pieces, rules, etc. Here's roughly how I modeled it (actual implementation removed since I don't want to take all the fun out of your coding):
public enum PieceType {
None, Pawn, Knight, Bishop, Rook, Queen, King
}
public enum PieceColor {
White, Black
}
public struct Piece {
public PieceType Type { get; set; }
public PieceColor Color { get; set; }
}
public struct Square {
public int X { get; set; }
public int Y { get; set; }
public static implicit operator Square(string str) {
// Parses strings like "a1" so you can write "a1" in code instead
// of new Square(0, 0)
}
}
public class Board {
private Piece[,] board;
public Piece this[Square square] { get; set; }
public Board Clone() { ... }
}
public class Move {
public Square From { get; }
public Square To { get; }
public Piece PieceMoved { get; }
public Piece PieceCaptured { get; }
public PieceType Promotion { get; }
public string AlgebraicNotation { get; }
}
public class Game {
public Board Board { get; }
public IList<Move> Movelist { get; }
public PieceType Turn { get; set; }
public Square? DoublePawnPush { get; set; } // Used for tracking valid en passant captures
public int Halfmoves { get; set; }
public bool CanWhiteCastleA { get; set; }
public bool CanWhiteCastleH { get; set; }
public bool CanBlackCastleA { get; set; }
public bool CanBlackCastleH { get; set; }
}
public interface IGameRules {
// ....
}
The basic idea is that Game/Board/etc simply store the state of the game. You can manipulate them to e.g. set up a position, if that's what you want. I have a class that implements my IGameRules interface that is responsible for:
Determining what moves are valid, including castling and en passant.
Determining if a specific move is valid.
Determining when players are in check/checkmate/stalemate.
Executing moves.
Separating the rules from the game/board classes also means you can implement variants relatively easily. All methods of the rules interface take a Game object which they can inspect to determine which moves are valid.
Note that I do not store player information on Game. I have a separate class Table that is responsible for storing game metadata such as who was playing, when the game took place, etc.
EDIT: Note that the purpose of this answer isn't really to give you template code you can fill out -- my code actually has a bit more information stored on each item, more methods, etc. The purpose is to guide you towards the goal you're trying to achieve.
Here is my idea, for a fairly basic chess game :
class GameBoard {
IPiece config[8][8];
init {
createAndPlacePieces("Black");
createAndPlacePieces("White");
setTurn("Black");
}
createAndPlacePieces(color) {
//generate pieces using a factory method
//for e.g. config[1][0] = PieceFactory("Pawn",color);
}
setTurn(color) {
turn = color;
}
move(fromPt,toPt) {
if(getPcAt(fromPt).color == turn) {
toPtHasOppositeColorPiece = getPcAt(toPt) != null && getPcAt(toPt).color != turn;
possiblePath = getPcAt(fromPt).generatePossiblePath(fromPt,toPt,toPtHasOppositeColorPiece);
if(possiblePath != NULL) {
traversePath();
changeTurn();
}
}
}
}
Interface IPiece {
function generatePossiblePath(fromPt,toPt,toPtHasEnemy);
}
class PawnPiece implements IPiece{
function generatePossiblePath(fromPt,toPt,toPtHasEnemy) {
return an array of points if such a path is possible
else return null;
}
}
class ElephantPiece implements IPiece {....}
I recently created a chess program in PHP (website click here, source click here) and I made it object oriented. Here are the classes I used.
ChessRulebook (static) - I put all my generate_legal_moves() code in here. That method is given a board, whose turn it is, and some variables to set the level of detail of the output, and it generates all the legal moves for that position. It returns a list of ChessMoves.
ChessMove - Stores everything needed to create algebraic notation, including starting square, ending square, color, piece type, capture, check, checkmate, promotion piece type, and en passant. Optional additional variables include disambiguation (for moves like Rae4), castling, and board.
ChessBoard - Stores the same information as a Chess FEN, including an 8x8 array representing the squares and storing the ChessPieces, whose turn it is, en passant target square, castling rights, halfmove clock, and fullmove clock.
ChessPiece - Stores piece type, color, square, and piece value (for example, pawn = 1, knight = 3, rook = 5, etc.)
ChessSquare - Stores the rank and file, as ints.
I am currently trying to turn this code into a chess A.I., so it needs to be FAST. I've optimized the generate_legal_moves() function from 1500ms to 8ms, and am still working on it. Lessons I learned from that are...
Do not store an entire ChessBoard in every ChessMove by default. Only store the board in the move when needed.
Use primitive types such as int when possible. That is why ChessSquare stores rank and file as int, rather than also storing an alphanumeric string with human readable chess square notation such as "a4".
The program creates tens of thousands of ChessSquares when searching the move tree. I will probably refactor the program to not use ChessSquares, which should give a speed boost.
Do not calculate any unnecessary variables in your classes. Originally, calculating the FEN in each of my ChessBoards was really killing the program's speed. I had to find this out with a profiler.
I know this is old, but hopefully it helps somebody. Good luck!
Related
I have read from other sources that it isn't a good idea for objects to know about each other especially the ones on same level. It should be more like hierarchy.
My problem is quite unique as I haven't figured out a way around it. Also I have been unlucky to come across any topic that addresses my issue specifically.
The problem
I am building a chess app and I am constructing the model of the app. Right now I have an abstract objects like for example Piece which other pieces like Queen, Knight and the rest would inherit from.
I also have a Board class which handles all board model and state of a game. Now each of my piece has a generateMove() method to calculate possible moves from their position and to do this they need to know the state of the board. Also the Pieces are been instantiated by Board at startup.
Question
Do I go ahead and instantiate Pieces by e.g
public class ChessBoard{
Boardbit = 64bit
Knight = new Knight(start_position, this)
//calculate
}
and then in Knight class method
public long generateMove(ChessBoard);
If no, what other ways can I go about it?
Making Chessboard to know the Knight and vise versa is not elegant. I agree with you in this point. Kind of sticking with the 'Tell don't ask' rule forces the 'higher level' element, in this case the chessboard, to tell the piece to move while providing all the required information. The Chessboard itself doesn't know which piece is moving (in this case of predicting possible moves for all pieces), but for sure never knows any details about how a piece can move or is allowed to move. This is just one possible solution using sort of Strategy Pattern. (The Visitor or some similar pattern could also be used here):
Main() {
chessboard = new Chessboard()
PiecesCollection = new PiecesCollection(new Knight(KnightStrategy, Color.Black))
chessboard.AddPieces(PiecesCollection)
CollectionOfAllPossibleMoveCollections = chessBoard.CallculateAllPossibleMoves()
Move selectedMove = ShowOrSelectMove(CollectionOfAllPossibleMoveCollections)
chessboard.ExecuteMove(selectedMove)
}
public class Chessboard{
// fields
PiecesCollectionWhite
// where 'PiecesCollectionWhite' is a collection of `Piece`
PiecesCollectionBlack
// where 'PiecesCollectionBlack' is a collection of `Piece`
CurrentlyVisitedPositionsCollection
// where 'CurrentlyVisitedPositionsCollection' is a collection of `Position`
// methods
AddPieces(PiecesCollection, Color)
CallculateAllPossibleMoves(Color) {
CollectionOfPossibleMoveCollections =
FOREACH Piece IN PiecesCollection OF Color
DO Piece.CalculateMoves(this.CurrentlyVisitedPositionsCollection)
return CollectionOfAllPossibleMoveCollections // where 'CollectionOfAllPossibleMoveCollections ' is a collection that holds a collection of `Move` of which each nested collection represents the possible moves of a chess piece.
}
ExecuteMove(Move) {
RemovePieceFromBoardIfNecessary(Move.ToPosition)
}
}
public class Piece
{
// fields
Strategy
Position
Color
// methods
CallculateMoves(CurrentlyVisitedPositionsCollection) {
PossibleMovesCollection = this.Strategy.Execute(CurrentlyVisitedPositionsCollection, this.Position)
return PossibleMovesCollection where `PossibleMovesCollection` is a collection of `Move`
}
}
public class Knight extends Piece
{
ctor(Strategy, Color)
}
public class Stragtegy
{
abstract Execute(currentPosition, allPiecesPositions) : PossibleMovesCollection
}
public class KnightStrategy extends Strategy
{
Execute(currentPosition, allPiecesPositions) {
PossibleMovesCollection = ApplyKnightMoveAlgorithm()
return PossibleMovesCollection
}
private ApplyKnightMoveAlgorithm() : PossibleMovesCollection
}
public class Move
{
Position fromPosition
Position toPosition
}
public class Color
{
Black
White
}
public class Position
{
Color
xCoordinate
yCoordinate
}
This is just a sketch and not a complete example. There is some state information or operations missing. E.g. maybe you would have to store the Color that was moved last on the chessboard.
Since the Chessboard returns all possible moves (information about all currently visited coordinates) you can easily enhance the algorithm by implementing some intelligence to predict the best possible moves from this information. So before the controller or in this case the Main() will call Chessboard.ExecuteMove(Move) it could make a call to PredictionEngine.PredictBestMove(CollectionOfAllPossibleMoveCollections).
Much better is to have method generateMove(boardState), so your Board should call whatever piece you have and pass them the necessary information for such task. It can be used even for some optimization as board can pregenerate some good-to-use structure each round only once and then pass it to all the pieces (like some 2d array).
Suppose we take the case of building a Vending machine using OOP principles.
Let us suppose we have an abstraction called VendingMachine.
class VendingMachine {
List<Slot> slots; //or perhaps a 2-d matrix of slots
}
The VendingMachine class has a list of slots and each slot may have some capacity (to model say 5 items one behind the other).
Now how do I associate a Value ($) to a slot. Clearly each slot will have the same item so each slot should be associated with same value (or better an abstraction representing the item, say Item class).
But in terms of responsibility, the VendingMachine class should only be able to eject an item, or throw an exception when trying to eject an item from an empty slot. I think that it is not the responsibility of VendingMachine class to know what is the value of a particular slot.
How do I design this elegantly? Is there some design pattern that comes to your mind.
My solution is to create a class MoneyManager.
class MoneyManager {
MoneyManager(VendingMachine vm);
Pair<Slot, Item> mapping;
}
class Item {
int itemCode;
BigDecimal value;
}
Even if you think that the modelling is wrong, what I am more interested in knowing is how do you decouple 2 classes like that.
For example if you design a car parking lot, a class Vehicles should have information on how much space it takes (number of spots). A ParkingLot has information how much spots it has.
But I don't want the car to know in which ParkingLot it is parked and in which spot. Similarly I don't want the ParkingLot to maintain the state of what cars are parked and where. Should there be an intermediate class ParkingManager which maintains this state for a clean design?
I think what you need is some kind of logic like a wharehouse administration.
Supposing all the slots and items are of the same size, you could probabbly break it out like this.
public class Item
{
public string Name { get; set; }
public string Category { get; set; }
}
public class Slot
{
public int Capacity { get; set; }
public List<Item> Items { get; set; }
}
public class Warehouse
{
public List<Slot> Slots { get; set; }
}
This is going to manage the basic distribution of the items in the warehouse. Warehouse class is going to manage the list of slots and Slot class is going to manage the list of items it contains.
Probably, if you need, you could add sizes and locations and add some logic of what kind of items fit the different slots.
Sometimes when you create a class you can add there several properties (new data members) that you are not certain if you want to do or not. For example, I have a casino slots game. I have tiles and tiles are spinning on different reels. So once 3 tiles come on the same line then player wins 3$, 4 tiles - 4$ and 5 tiles - 5$ for tile A and for tile B player wins 5$, 10$, 20$ accordingly. Should, for example, each tile store the data of its reward or there should be a reward manager for checking how many tiles are consecutive next to each other to give the reward to the player?
Please note that I don't want to have such a situation. But I find me many times thinking "Should I add this data, and consequently, corresponding logic the my class or not?". I worry about single responsibility principle when I want to have different managers for such things, but on the other hand I came to a situation to create several singletons or singleton-like classes.
Well, this sounds a lot like a use case for the Strategy Pattern.
As far as I am concerned (never been to a casino, since they're prohibited here in my country), most of slot machines work the same way.
So, you might think of one implementation as (pseudo-java code):
class Figure {
private String representation;
}
class Slot {
private Set<Figure> figures;
public Figure getRandom() {
// retrieve random figure from a slot
}
}
interface IRewardStrategy {
public int getReward(SlotMachine machine);
}
class OneFoldRewardStrategy implements IRewardStrategy {
public int getReward(SlotMachine machine) {
return machine.getCurrentLinedSlotsCount();
}
}
class TenFoldRewardStrategy implements IRewardStrategy {
public int getReward(SlotMachine machine) {
return 10 * machine.getCurrentLinedSlotsCount();
}
}
class SlotMachine {
private int slotCount;
private List<Slot> slots;
private List<Figure> currentRollResult;
private IRewardStrategy rs;
public SlotMachine(List<Slot> slots, IRewardStrategy rs) {
this.slots = slots;
this.rs = rs;
}
public void roll() {
// For each slot, get random figure
}
public int getTotalSlots() {
return slotCount;
}
public int getCurrentLinedSlotsCount() {
// Iterates over the current roll result and get the number of lined slots
}
public int getReward() {
this.rs.getReward(this); // delegates to the reward strategy
}
}
// Usage
SlotMachine machine = new SlotMachine(..., new TenFoldRewardStrategy());
machine.roll(); // suppose this give 3 slots in a row...
print(machine.getReward()); // This will yield 30
Attention: This is a very bare code, just to give you an idea, it has several problems.
Language doesn't matter, it is generic object-oriented question(take java/C# etc). Take a simple concept.
A Person has a Car. The Person can drive the Car. Car doesn't usually drive or wander around, right? ``
But, usually in codes, we see methods like myCarObject.Drive().
Now when a Person is introduced, and the Person drives the car:
======================= First Way =================================
class Car{
int odometer;void drive(){ odometer++; }
}
class Person{
void driveCar(Car c) { c.drive(); }
}
========================================================================
================================ Alternative Way =======================
public Car{
int odometer; // car doesn't do the driving, it's the person, so no drive()
}
public Person{
void driveCar(Car c) { c.odometer++; }
}
========================== and other ways....============================
===========================================================================
So, my question is clear: what is the best way to design/implement/name methods in similar cases?
It's a bit difficult to make simplified examples like that make any sense, but here is an attemt:
A Car class would generally contain methods for the things that the object can do by itself with the information that it has, for example:
public class Car {
private bool engineOn;
public int Speed { get; private set; }
public void Start() { engineOn = true; Speed = 0; }
public void Accelerate() { Speed++; }
public void Break() { if (Speed > 0) Speed--; }
public void Stop() { Speed = 0; engineOn = false; };
}
A Person class would would manage a car by controlling the things that the car itself is not aware of in its environment. Example:
public class Person {
public void Drive(Car car, int speedLimit) {
car.Start();
while (car.Speed < speedLimit) {
car.Accelerate();
}
while (car.Speed > 0) {
car.Break();
}
car.Stop();
}
}
There are of course many different variations of how you can use OO in each situation.
If you wish to express your logic in a way that closely resembles human language semantics, you'll want to invoke an action or function on an entity which is logically capable of carrying it out.
When behavior cannot be placed on an object (in the sense that it has state), you put it in a Service or Utility class, or some similar construct. Authenticate is a classic example of something that doesn't make much sense to invoke on a user, or on any other object. For this purpose, we create an AuthenticationProvider (or service, whichever you prefer).
In your scenario of a Person and a Car, it's one object invoking behavior on another. person.Drive(car) would therefore make the most sense.
If a Person owns a Car (and a Car is always owned by a Person), then person.Drive() might be the only thing you need to do. The Drive() method will have access to the properties of person, one of which is its car.
An important thing to note here is the concept of loose coupling. In more complex scenario's, you don't want to all sorts of cross-references within your model. But by using interfaces and abstractions you'll often find yourself putting methods on objects where they don't really belong from a real-world perspective. The trick is to be aware of, and utilize a language's features for achieving loose coupling and realistic semantics simultaneously.
Keeping in mind that in a real application you'll have the bootstrapping code tucked away elsewhere, here is an example of how that might look like in C#:
We start off by defining interfaces for the things that can transport (ITransporter), and the things that can be transported (ITransportable):
public interface ITransportable
{
void Transport(Transportation offset);
}
public interface ITransporter
{
void StartTransportation(ITransportable transportable);
void StopTransportation(ITransportable transportable);
}
Note the Transportation helper class which contains the information necessary to re-calculate the current location of an ITransportable after it has been transported for a certain period of time with a certain velocity and whatnot. A simple example:
public class Transportation
{
public double Velocity { get; set; }
public TimeSpan Duration { get; set; }
}
We then proceed to create our implementations for these. As you might have guessed, Person will derive from ITransportable and Car derives from ITransporter:
public class Person : ITransportable
{
public Tuple<double, double> Location { get; set; }
private ITransporter _transporter;
void ITransportable.Transport(Transportation offset)
{
// Set new location based on the offset passed in by the car
}
public void Drive<TCar>(TCar car) where TCar : ITransporter
{
car.StartTransportation(this);
_transporter = car;
}
public void StopDriving()
{
if (_transporter != null)
{
_transporter.StopTransportation(this);
}
}
}
Pay close attention to what I did there. I provided an explicit interface implementation on the Person class. What this means is that Transport can only be invoked when the person is actually referenced as an ITransportable - if you reference it as a Person, only the Drive and StopDriving methods are visible.
Now the Car:
public class Car : ITransporter
{
public double MaxVelocity { get; set; }
public double Acceleration { get; set; }
public string FuelType { get; set; }
private Dictionary<ITransportable, DateTime> _transportations = new Dictionary<ITransportable, DateTime>();
void ITransporter.StartTransportation(ITransportable transportable)
{
_transportations.Add(transportable, DateTime.UtcNow);
}
void ITransporter.StopTransportation(ITransportable transportable)
{
if (_transportations.ContainsKey(transportable))
{
DateTime startTime = _transportations[transportable];
TimeSpan duration = DateTime.UtcNow - startTime;
var offset = new Transportation
{
Duration = duration,
Velocity = Math.Max((Acceleration*duration.Seconds), MaxVelocity)/2
};
transportable.Transport(offset);
_transportations.Remove(transportable);
}
}
}
Following the guidelines we set earlier, a Car will not have any (visible) methods on it, either. Unless you explicitly reference it as an ITransporter, which is exactly what happens inside of the Person's Drive and StopDriving methods.
So a Car here is just a Car. It has some properties, just like a real car, based on which you can determine a location offset after a person drove it for a certain amount of time. A Car cannot "Drive", "Start", or anything like that. A Person does that to a Car - a Car does not do that to itself.
To make it more realistic you would have to add all sorts of additional metadata that affect a Car's average velocity over a certain period of time on a certain route. Truth is, you probably won't end up modeling something like this anyway. I stuck with your model just to illustrate how you could retain natural language semantics if you were working with objects that make it challenging to do so.
An example of how these classes may be used by a client:
Person person = new Person();
Car car = new Car();
// car.Transport(); will not compile unless we explicitly
// cast it to an ITransporter first.
// The only thing we can do to set things in motion (no pun intended)
// is invoke person.Drive(car);
person.Drive(car);
// some time passes..
person.StopDriving();
// currentLocation should now be updated because the Car
// passed a Transportation object to the Person with information
// about how quickly it moved and for how long.
var currentLocation = person.Location;
As I already eluded before, this is by no means a good implementation of this particular scenario. It should, however, illustrate the concept of how to solve your problem: to keep the logic of "transportation" inside of the "transporter", without the need to expose that logic through public methods. This gives you natural language semantics in your client code while retaining proper separation of concerns.
Sometimes you just need to be creative with the tools you have.
In second case, it's like you're saying that the task of driving a car consist in incrementing the odometer. It's clearly not the driver's business, and a violation of encapsulation. The odometer should probably be an implementation detail.
In first case, the car maybe does not drive itself, but it advances, so you could use another verb. But car.advance() is maybe not how a Person drives cars... Even if it was thru vocal commands, the decoding of the command would probably result in a sequence of more basic commands.
I very much like the answer of Guffa which tries to address what driving a car could mean. But of course, you may have another context...
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
I've found a lot of information on object oriented programming, but none of it seems to go into much detail. They always give you the shape example where cirlce, square, and retangle implement the interface. That's easy. I'm looking for something more real life and deeper into the process.
Does anybody have any good resources that are pretty in-depth? Or even code samples would be helpful.
That's a very broad question... Here's just a few links for you:
http://en.wikibooks.org/wiki/Object_Oriented_Programming
http://www.amazon.com/Object-Oriented-Programming-Peter-Coad/dp/013032616X
#Frankie - I've edited this for you having seen your comment. You're question is still very broad, but I'll try to provide a quick (very loosely thought) example of modeling some objects. The language I'll use is C#, though you can do it any OOP language you like.
We use Interfaces and Base Classes to represent very basic models. One of the defining differences between and Interface and a Base Class is that an Interface cannot be instantiated (think of it as a blueprint that cannot physically exist, just a design on paper)... a Base Class however can be instantiated (it can exist, and might be considered a prototype). Let's go from there...
Say we want to model vehicles... airplanes, cars, motorcycles, bicycles, etc. In our modeling brain we recognize that a Vehicle is the root of everything. Let's start then by defining a blueprint that works for all types of vehicles. For that we'll use an Interface
interface IVehicle
{
string Make;
string Model;
int Year;
}
Our interface now says, any object we build that implements this interface must have a Make, Model, and Year property. Now cars, bikes, motorcycles, etc. pop into our head, and we want to make classes for them... but we realize, lots of these vehicles have things in common. Let's make a prototype for all LandVehicles, and for that we'll use a Base Class that implements our blueprint interface IVehicle
public class LandVehicle : IVehicle
{
// We must physically implement the required members of the interface.
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
// Then we can add things specific to land vehicles.
public int NumberOfWheels { get; set; }
public int TopSpeed { get; set; }
}
Now we have a prototype to build from. Let's design a Car and a Cycle
public class Car : LandVehicle
{
// because LandVehicle is a real object, we do not have to re-implement its memebers,
// we can just add to them:
public int MaxPassengers { get; set; }
public bool IsLuxury { get; set; }
public string FuelType { get; set; }
}
public class Bicycle : LandVehicle
{
public string Type { get; set; } // mountain, race, cruiser, etc.
public int NumberOfGears { get; set; }
}
With that, we can instantiate Cars and Bicycle objects... but by using Base Classes, we could create many other types of LandVehicles without having to add our basic properties to each one. This is one of the things than makes OOP so extensible.
Furthermore with our Interface, we left it open enough to make other base classes, perhaps WaterVehicles, AirVehicles, etc... and thus classes that derive from them.
This is just the very tip of the iceberg, and a rather off-the-top-of-my-head example, but it should get you started. If you have more specific questions or a specific scenario you'd like to use as context, let me know and I'll help out more.