For a computer game the rewards for the player as he/she progresses through the game are:
Entry level: Lamp worth n points
Intermediate level: magic mat m points
Advanced level: treasure chest which can be filled with lamps or mats
Here we apply the composite pattern
Client: computer game
I believe that the component are the levels (entry, intermediate and advanced)
Composite: Rewards
Leaf: lamp,magic mat,chest
Operation: getPoints(), fillChest()
Are these correct?
A Composite is typically a tree/collection of objects. The same operations you can perform on the individual objects can also be performed on the collection. Additionally, the object can typically be stand-alone or contain more objects and operations on the containing object will be performed on the objects is contains.
So if that is how you plan to use your Composite objects, then yes it sounds correct.
I agree with Brady. All depends on how you plan to use your Composite objects. I used the composite design pattern for an RPG attributes system. All you need to do is create different attributes and assign each of them an Attribute instance. After that, it's all about adding and removing bonuses to it through the already created methods.
Related
In the below UML diagram, Account has an aggregation of Orders. Based on most online resources, this would typically mean Account class has something similar to a List as an instance.
But in reality, for a real world web app with persistent storage, that is not usually how the Account Class would be. It won't have a list of orders as instance. Instead some other controller class will just query a datastore asking for all Orders belonging to an Account. So in a UML class diagram for such an app, is this still the right way to represent relations? The cardinality and maybe the concept of aggregation looks right from a database entity perspective. Just that the diamond makes no sense from a Class perspective.
Or should it show a DataStore/DataManager with a getOrdersForAccount() method and connect it to Account class and Orders class through a dependency relation (dotted line with arrow) ?
This depends on what you want to represent.
The class model you have already would be sufficient as a logical domain model, expressing the logical relationships between entities in your domain. This might not be how you implement your software in code precisely, but it will guide you (and others) in understanding the entities and their relationships without getting bogged down in that implementation detail. At this level, your diagram may have a few design choices (strong aggregation for example is arguably a design choice, but it may not be, as is the use of enumerations and keys) but not that many and nothing that really detracts from the underlying logic. If anything, you could loose some design choices here and improve the expression of logic.
What you may also want is to provide a representation of how the OO code is implemented physically as well. This would be an additional class diagram that shows more precisely the implementation detail. You will have far more design choices in this diagram -- whether to use a collection or not for orders (e.g. a list or some other collection type class), what your data access patterns are (Adapters, Managers, ORMs etc.). At this level you will most likely loose the strong aggregate notation, as at this level we are talking about classes referencing each other which is most simply denoted using basic associations. You might want to use arrows and/or dot-notation to indicate end ownership and reference directions so that it's more clear what the relationships between classes are.
So, I think your question is a classic question about levels of abstraction in models and analysis vs design. Thanks for asking it!
The aggregation just means: "if you delete the account you need to delete the orders as well".
I also recommend to just leave the aggregation away (for most cases) since it only adds little extra semantics to your model. In this case it seems obvious to delete the order when the account is deleted. The only thing the aggregation added here is (as in most cases) some confusion or some futile discussions about the worth of that diamond.
If you have a domain where the filled diamond is used it should be documented in the modeling rules. When using the shared aggregation the documentation is even mandatory since there is no semantics per se in the specs (see box on p. 110 of UML 2.5).
It depends on how deep you want to go with UML design.
If you target code generation from UML then you probably need to add the class you mentioned.
It would look a lot like Registry Pattern:
UML Diagram
You can add abstraction so you can change implementation of your DataManager (if your DataManager is third-party then just call the API from DataManagerImplementation).
After that, depending on your implementation, once you have the list, if you need to keep it then add the association Account -> Order, if you can live with the list on the stack then you are good to go.
C++ instanciation example:
DataManagerImplementation *db = new DataManagerImplementation();
// Dependency injection
Account *acc = new Account(db);
Then in 'Account' class:
Account::Account(DataManager *db)
{
// Fetch list at creation
// Here 'orders' could be a member
m_db = db;
vector<Order*> *orders = m_db->GetOrders(this);
}
PS: I also recommend to put arrow (direction) on association/aggregation, otherwise it implies that the association is bi-directional and so that account has a pointer to an order list, and every order also has a pointer to an account, and I am not sure this is needed.
To edit PlantUML: http://www.plantuml.com/plantuml/png/SoWkIImgAStDuN99B4dqJSnBJ4yjyimjo4dDJSqhIIp9pCzJqDMjiLFmBqf9BK9ImuKk05Hcfw2afGHHYIbjfL2McboINsG3bj6oKz1oJoq1iuir79EJyqlpIZIve0m5a566IfYMEgJcfG0T2m00
In the book Design Patterns : Elements of Reusable Object-Oriented Software, It says:
"The composite design pattern composes objects into tree structures to represent part-whole hierarchies."
In wikipedia the composite design pattern is defined as :
"The composite pattern describes a group of objects that is treated the same way as a single instance of the same type of object. The intent of a composite is to "compose" objects in to tree structures to represent part-whole hierarchies."
Picture of composite design pattern:
If my Composite stores components as directed acyclic graph( for example it only stores components which are sources of DAC in queue data structure and those sources have references to another components and so on... ) which is not tree because it violates some tree structure condition. Can I still say that I have used composite design pattern?
You can store your components in any data structure you like. The point is that each of your components can also be treated as “whole”.
The easiest example is a CompositeView object which contains subviews. CompositeView is a View and its subview objects are also Views. So you have a common interface/abstract class for your objects. It doesn’t matter at all what data structure is used to store subviews.
In the mentioned tree-like structure your list/set/dag/whatever... of components defines a set of child nodes for a given parent node.
Another example could be with Brick, Wall, House, Block. A Wall is composed by many Bricks; in same way an House is composed by four Walls (for the example leave me say that an House doesn't have a roof) and a Block is composed by many Houses. Brick in the Composite pattern represents a Leaf, instead Wall,House,Block are specialisations of Composite, but all can be considered as ConstructionComponents (or ConstructionEntities).
The image shows the logistics of the Warehouse. Very very simplistic. What is its concept: There are documents: ReceivingWayBill, DispatchingWaybill, ReplacementOrder.
They interact with the main classes: Warehouse, Counterparty, Item.
And the Register class: ItemRemainsInWarehouse. It turns out, the document is confirmation of the operation, reception, sending, and so on. The Register simply stores information about the number of remaining goods.
If you miss a lot of problems of this scheme, such as: the lack of generalization, getters and setters and a heap of everything else.
Who can tell: the relationship between classes, and there is concrete aggregation everywhere, are placed correctly, or can we somehow consider the association in more detail?
It is so hard (maybe impossible) to correct your whole model with provided explanation. I give some improvements.
You should put Multiplicity of you relationships. They are so important. In some relationship, you have 1 (ReplacementOrder , Warehouse) and some of your relatioships are maybe * (Item , ReceivingWayBill)
You put Aggregation between your classes and we know that Aggregation is type of Association. You can put Associations too. You can find a lot of similar questions and answers that explain differences between Association and Aggregation (and Composition). see Question 1, Question 2 and Question 3. But I recommend this answer.
I think, there is NOT a very significant difference between Aggregation and Association. See my example in this question.
Robert C. Martin says (see here):
Association represents the ability of one instance to send a message to another instance.
Aggregation is the typical whole/part relationship. This is exactly the same as an association with the exception that instances
cannot have cyclic aggregation relationships (i.e. a part cannot
contain its whole).
Therefor: some of your relationships are exactly an Aggregation. (relationship between Item and other classes). Your Counterparty has not good API definition. Your other relationships is about using Warehouse class. I think (just guess) the other classes only use Warehouse class services (public methods). In this case, they can be Associations. Otherwise, if they need an instance of Warehouse as a part, they are Aggregations.
Aggregation is evil!
Read the UML specs about the two variants they introduced (p. 110):
none: Indicates that the Property has no aggregation semantics. [hear, hear!]
shared: Indicates that the Property has shared aggregation semantics. Precise semantics of shared aggregation varies by application area and modeler.
composite: Indicates that the Property is aggregated compositely, i.e., the composite object has responsibility for the existence and storage of the composed objects (see the definition of parts in 11.2.3).
Composite aggregation is a strong form of aggregation that requires a part object be included in at most one composite object at a time. If a composite object is deleted, all of its part instances that are objects are deleted with it.
Now, that last sentence clearly indicates where you should use composite (!) aggregation: in security related appications. When you delete a person record in a database you need to also delete all related entities. That often used example with a car being composed of motor, tires, etc. does not really fit. The tires do not vanish when you "delete" the car. Simply because you can not delete it. Even worse is the use of a shared composite since it has no definition per definition (sic!).
So what should you do? Use multiplicities! That is what people usually want to show. There are 0..n, 1, etc. elements related to to the class at the other side. Eventually you name these by using roles to make it explicit.
If you consider DispatchingWaybill and ReceivingWaybill it looks like those are association classes. With the right multiplicities (1-* / *-1) you can leave it this way. (Edit: note the little dots at the association's ends which tell that the class at the opposite has an attribute named after the role.)
Alternatively attach either with a dashed line to an association between the classes where they are currently connected to.
I am trying to develop a data model for a very diverse set of interconnected objects. As the application matures, the types of objects supported will increase significantly. I want to avoid having to modify the model/schema whenever new object types are added.
As a simple example, let's say I'm starting with a model of people and buildings. A building can have multiple owners; a person can own multiple buildings; a person can live in a house and work in an office... Future versions might add cars and corporations. Cars can have owners, corporations can manufacture cars, people can work for corporations, etc. Most of the relationships will be many-to-many, some will be one-to-many, very few will be one-to-one.
While concepts like "owner", "employer", or "manufacture" can be considered properties of a "building", "corporation", or "car" object, I don't want to redefine the data model to support a new property type.
My current idea is to model this similar to a graph, where each piece of data is its own node. The node object would be very simple:
Unique identifier
Name (human representation)
Node type
Relationships
Extending the previous example, the possible node types would be:
Person
Car
Company
Building
A relationship would be:
Node A
Node B
Relationship type - uses, owns, has, is, etc
I have a few questions:
Are there any drawbacks to this approach?
Is there an existing pattern or model that describes this?
Are there better approaches?
Is there an existing pattern or model that describes this?
What you describe sounds like a network data model, also known as an object or object-oriented data model.
Are there any drawbacks to this approach?
Your model doesn't support ternary and higher relationships. It also creates fixed access paths between nodes, which supports node-to-node navigation, but which can make many queries convoluted. I also don't see any support for subtyping.
Without composite determinants, some situations will be difficult to model or query. You don't support predicates like (Object, Language) -> Name (or (Company, Role) -> Person, etc). One way is to create special relationship types, but your model is going to be asymmetric and more complicated to query.
Are there better approaches?
The relational model of data handles n-ary relations between object types / domains, and allows for the representation of complex predicates. N-ary relations mean it supports object hypergraphs, and user-defined joins mean ad-hoc access paths. Composite determinants are supported, and most implementations support a variety of integrity constraints.
In particular, look at Object-Role Modeling (http://www.orm.net, https://www.ormfoundation.org).
I want to avoid having to modify the model/schema whenever new object types are added.
Try doing a web search for "universal schema for knowledge representation". Facts about the world aren't limited to simple atomic observations like "John Smith has a dog named Spot". We have to deal with facts like "Company A is not allowed to distribute product B in regions within 100km of point C after date D if that product contains ingredients E or F". The most powerful knowledge representation we've got so far is natural language, and as far as I know we don't yet have a simple model of its structure.
I'm currently reading Ologs: A Categorical Framework For Knowledge
Representation. Perhaps this will be of interest to you too.
Can composition be bidirectional in a way that both classes are aware of each other?
And if not, what is the default direction of composition?
You should distinguish navigability and aggregation. Arrow and diamond.
Arrow A->B means only that B is reachable from A in some simple way. If A contains a composition of B, it means that
the composite object has responsibility for the existence and storage
of the composed objects (parts).
(citation from OMG Unified Modeling Language TM (OMG UML) - p.109)
So, can composition have bi-directional navigability?
Yes. It is quite normal.
If, for example, you have decided to destroy B in some of its functions, you MUST reach A and destroy it from there. So, composition has bi-directional navigability often enough. Notice, that bi-directional navigability, according to both current and coming UML standards, is shown as line without arrows on both sides. Both-sided arrow is deprecated. THAT is the reason you won't see it often.
Can the composition itself be bi-directional? Can we see black diamonds on both sides of an association?
No, of course this sort of association cannot be mutual, for it is impossible for B to be created in A only and, simultaneously, for A to be created in B only.
What is interesting, the shared aggregation (empty diamond) cannot be mutual, too, but here the limitation is not inherent, it is simply forbidden by UML standard.
Yes, Composition does not add constraints with regards to the navigability of the association.
More info on the difference between Accociation, Composition and Aggregations can be found here: UML Composition vs Aggregation vs Association
From https://www.lucidchart.com/pages/uml/class-diagram:
Bidirectional associations are the default associations between two classes and are represented by a straight line between two classes. Both classes are aware of each other and of their relationship with each other. In the example above, the Car class and RoadTrip class are interrelated. At one end of the line the Car takes on the association of "assignedCar" with the multiplicity value of 0..1 which means that when the instance of RoadTrip exists, it can either have one instance of Car associated with it or no Cars associated with it. In this case, a separate Caravan class with a multiplicity value of 0..* is needed to demonstrate that a RoadTrip could have multiple instances of Cars associated with it. Since one Car instance could have multiple "getRoadTrip" associations-- in other words, one car could go on multiple road trips--the multiplicity value is set to 0..*
In the past I had the same opinion as Gangnus with
So, can composition have bi-directional navigability?
But following some recent discussion I had a more detailed look into the UML specs. And simply, that statement is not true (only partially). Let's look into the UML 2.5 specs. On p. 110 it is stated
Sometimes a Property is used to model circumstances in which one instance is used to group together a set of instances; this is called aggregation. To represent such circumstances, a Property has an aggregation property, of type AggregationKind; the instance representing the whole group is classified by the owner of the Property, and the instances representing the grouped individuals are classified by the type of the Property. AggregationKind is an enumeration with the following literal values:
[omitting shared aggregation]
composite: Indicates that the Property is aggregated compositely, i.e., the composite object has responsibility for the existence and storage of the composed objects (see the definition of parts in 11.2.3).
Composite aggregation is a strong form of aggregation that requires a part object be included in at most one composite object at a time. If a composite object is deleted, all of its part instances that are objects are deleted with it.
Note my emphasis on the object/instance in the above text. So UML just talks of responsibility. If A composes B it will be responsible to delete B when it is destroyed itself. Vice versa B would be responsible for A's destruction. So, if you have references in both directions (i.e. diamonds on both sides) then you will be reponsible to delete the object on the other side. This of course works only if just one of both holds a reference to the other. If both would have a reference, it would not be possible to have a directed responsibility (because it's circular).
I still think that having composite aggregation on both sides is not really a good idea. But according to the specification it is possible.