Behavior in this context is refering to semantic behavior. For example, a Property on a class (i.e. attribute on a domain object) that is used to uniquely identify it has a behavior. While this is not represented in code directly. The expected behavior is that there will not be any duplicate values for that property. Something such as an Address which may have its own identity, but does not exist outside of the context of a Person Entity should still be moved out into it's own object. Thus promoting the Entity into an Aggregate Root.
Furthmore, a property such as Age has no context outside of a Person Entity, and as such, makes no sense to move into a different object. The context would be lost, and therefore you can safely determine that it is a value that is essential to the Person Entity. You could not locate the value otherwise. However that information could easily be stored in a separate location that the unique identifier, hence the confusing reference to behavior. Age could be a lazy loaded value.
So to answer your question. No, it does not violate the Single Responsibility Principle. It is mearly stating that a Person should have only person stuff, and not something like Address, which is more complex and related to a person, should exist as its own entity.
You could easily have operations specific to Address such as verification that it is a valid address. You may not know that at design time, but this whole concept is to break down objects into their smallest parts so that something like this is relatively simple when done after the fact.
Update:
1) In most cases this identity validation is done upon saving an object out to a data store. Which means that the code representing the entity validation exists, but it exists elsewhere. It usually exists with the code that is responsible for issuing the identity value. Which is why I state that the uniqueness is not represented directly in the code for the entity.
2) The correct statement would be that Age
is an attribute that has behavior. You would need to document the fact that Age is lazy loaded so that a developer consuming that property can make an accurate decision on how to consume that property
3) DateOfBirth
usually is a different object; A date object that already has predefined operations on it. In some languages the date object already has a whole domain model defined on it. For example in c# you can specifiy the timezone, if the date is UTC, add and subtract dates to get a timespan. So your assumption about moving DateOfBirth
would be correct.
4) If the only thing that MyEntity
does is delegation and cooridination then no it does not violate SRP. Its single responsibility is to delegate and coordinate and is refered to as the Facade pattern
One often missed detail of the Single Responsibility Principle is that the "reasons for change" are grouped by use-case actors (you can see a full explanation here).
So, in your example, the calculatePay
method will need to be changed whenever new types of Employees are required. Since one type of employee may have nothing to do with another, it would be a violation of the principle if you keep them together, since the change would affect different user-groups (or use-case actors) in the system.
Now, about whether the principle applies to functions: Even if you have a violation in only one method, you are still changing a class for more than one reason, so it is still a violation of SRP.
Best Answer
Of course, the YAGNI principle will tell you to apply SRP not before you really need it. But the question you should ask yourself is: do I need to apply SRP first and only when I have to actually change my code?
To my experience, the application of SRP gives you a benefit much earlier: when you have to find out where and how to apply a specific change in your code. For this task, you have to read and understand your existing functions and classes. This gets very much easier when all your functions and classes have a specific responsibility. So IMHO you should apply SRP whenever it makes your code easier to read, whenever it makes your functions smaller and more self-describing. So the answer is yes, it makes sense to apply SRP even for new code.
For example, when your printing code reads a document, formats the document and prints the result to a specific device, these are 3 clear separable responsibilities. So make at least 3 functions out of them, give them according names. For example:
Now, when you get a new requirement to change document formatting or another one to print to PDF, you know exactly at which of these functions or locations in code you have to apply changes, and even more important, where not.
So, whenever you come to a function you don't understand because the function does "too much", and you are not sure if and where to apply a change, then consider to refactor the function into separate, smaller functions. Don't wait until you have to change something. Code is 10x more often read than changed, and smaller functions are much easier to read. To my experience, when a function has a certain complexity, you can always split the the function into different responsibilities, independent of knowing which changes will come in the future. Bob Martin typically goes a step further, see the link I gave in my comments below.
EDIT: to your comment: The main responsibility of the outer function in the example above is not to print to a specific device, or to format the document - it is to integrate the printing workflow. Thus, at the abstraction level of the outer function, a new requirement like "docs should not be formatted anymore" or "doc should be mailed instead of printed" is just "the same reason" - namely "printing workflow has changed". If we talk about things like that, it is important to stick to the right level of abstraction.