Hierarchy and design patterns question
I'm modeling a document class for a system. The document can be one of two types: in or out.
If the type is in, the document has a sender. The sender can be one of two types: person or company.
If the type is out, the document has a receiver. The receiver can be one of three types: person, company, department.
I'm not sure if it would be better to use a property with an enumerat开发者_如何学Pythonion for the type of document or to use hierarchy with a document base class and two classes for each type of document.
For the sender and receiver I'm not sure if hierarchy would be a good option because the three types don't have anything in common (person, company, department) and how to avoid an invalid sender.
It would be good if you can give me some advice about how to model the document class or if you can tell me about some design patterns that I should use.
Thanks in advance.
There are only a few differences between in and out, the same fields with the exception of sender and receiver. Also, the behavior is the same with a little changes.
There are no behavior for sender and receiver, the only they have to do is contain the correct object, for example sender can contain a person or a company but no a department because departments are not valid senders. Also, if sender contains a person, it can't contain a company because only one sender is accepted.
The main problem is how to read the sender o receiver when I get the document and I have to read that data. For example, if I have to read the sender and I use a enumeration with the kind of sender I have to do code like this if sender==person read person and assign it to a person else read a company and assign to a company. If I use inheritance how I avoid to use cast or how I know if the sender is a person or company without so much code or cast. Thanks again.
If you are using a language that allows objects to implement interfaces, then that would be a good way to deal with the complex type relationships.
ISender could be implemented by Person and Company. IReceiver could be implemented by Person, Company, and Department.
This way the documents could hold a reference to a receiver, even though the three types don't have anything in common.
For the two types of documents, it depends a lot on how much functionality will be shared between them. If it is none, then there is no point in having a relationship at all. If they share a lot of functionality, then it may be worthwhile to include an abstract base class to implement it. If they are completely (or almost) the same, then a single class with an in/out flag might be a good idea.
Personally I don't see particularly advantages in modelling as a hierarchy the document class, because the are very little differences between in and out documents. Especially considering that if
If the type is in the document has a sender.
Even if it is implicitly it has also a receiver ( you). And the same is for out document. So, as far as I can know from the information you gave, I'd use enum for distinguish both Document(in and out) and types (person, company, department).
Try to follow kiss principle whenever possible.
Anyway you have also to take in considerations the type of operations you are going to do with the documents and the what additional data the have to store. If you see that in and out documents could increase their differences while your application will grow, so introduce a hierarchy may be a good idea. (Even in this case I will keep types separated, using some enum like structure to store them)
EDIT:
Regarding your comment: maybe there are. I don't know which language are you planning to use. But, for example, with Java every entity (person, company..) could be a class implementing an interface (for example an interface call Entity). Then using generics you can instantiate your class forcing the generic type to be an implementation of interface entity. Something like:
public interface Entity{...}
public class Document<T implements Entity>{}
Basically it boils down to this. If there's going to be if statements like
if(document.IsIncoming()){
do something
}elseif (document.SentToDepartment()) {
do something else
}
repeated more than a couple of places you'd be better of with some kind of a polymorphic solution (think abstract class or interface). Same thing with the sender and receiver types.
However, you don't need to subclass at the top. You can have a single document class with different behavior attached to it through polymorphism. Assume that printing behavior is different for incoming and outgoing documents. Then you create IPrinter interface and implement it in two classes as follows.
public class Document
{
DocumentType type;
IPrinter printer;
}
interface IPrinter{
print();
}
class IncomingPrinter :IPrinter{}
class OutgoingPrinter :IPrinter{}
Whenever it is decided that a document is going to be incoming (maybe when it is being created), you assign the IncomingPrinter. If there are multiple types of behavior that needs to be assigned like this usually a factory pattern is used. This way you localize the if(doc.IsIncoming()) statements to one place. Benefits of not repeating a decision at different places in the code is many.
I am quite against hierarchies here. Sender and reciever are clearly concepts that has some behaviour and carries some data. I would focus on these responsibilities and create entities Sender and Reciever. Later, if neccesary make subclasses of these like CompanySender. Basically dont polute Company entity with sender logic if your whole system isnt about senders and recievers (i kind of assume that it is not).
For modeling in vs. out documents, the best design is dependent on how you will typically be processing documents.
If you usually know at compile time whether a document is an in / out kind and other than sender vs. receiver there is a lot of shared properties and behavior, then a hierarchy would be nice (since you'd have methods which accept either InDocument or OutDocument which may be resolved at compile time, or even runtime depending on your language).
If on the other hand your documents are all mixed up and you are mostly processing them at runtime, then another solution (which I'll present shortly) may be cleaner. I suspect this is almost certainly true in regards to the sender and receiver kinds, which almost certainly don't warrant a hierarchy (though, depending on your language, you might need to use an interface marker in order to emulate the functional solution I am about to present). What you want in these cases is a kind of "enum with data". In an object-functional language like F#, you can combine record types and discriminant unions to model this well:
type PersonInfo = {Name:string ; Age:int}
type CompanyInfo = {Name:string ; Location:string}
type sender =
| Person of PersonInfo
| Company of CompanyInfo
type DepartmentInfo = {Name:string ; Floor: int}
type receiver =
| Person of PersonInfo
| Company of CompanyInfo
| Department of DepartmentInfo
type documentType =
| In of sender
| Out of receiver
type Document = {Type:documentType ; Title:string ; Body:string ; Date:DateTime } with
//example of how to process a Document using pattern matching
override this.ToString() =
match this.Type with
| In(s) ->
match s with
| sender.Person(info) -> sprintf "In / Person, extra info: Name = %s ; Age= %i" info.Name info.Age
| sender.Company(_) -> "In / Company"
| Out(r) ->
match r with
| receiver.Person(_) -> "In / Person"
| receiver.Company(_) -> "In / Company"
| receiver.Department(_) -> "In / Department"
//create a list of In and Out documents all mixed up
let documents =
[{Type = In(sender.Person({Name="John"; Age=3})); Title="My In Doc"; Body="Hello World"; Date=DateTime.Now}
{Type = Out(receiver.Department({Name="John"; Floor=20})); Title="My Out Doc"; Body="Testing"; Date=DateTime.MinValue}]
//partition documents into a list of In and Out Types
let inDocuments, outDocuments =
documents |> List.partition (function | {Type=In(_)} -> true | {Type=Out(_)} -> false)
精彩评论