|
Many patterns, such as those in the GoF book, aren't "architectural". They're more tactical, although they can help to make an architecture better.
Jim Coplien, who was well-known in the C++ world, got involved with patterns early on. Later, he said that there was too much focus on patterns. The emphasis needs to be on encapsulation, polymorphism, and inheritance. Patterns are the exception when those things, by themselves, don't produce loosely coupled code.
To understand a pattern, you need to have seen what code looked like before the pattern was applied, and after. Then, when you write or find code that looks like the "before" code, you'll know how the pattern can improve it. That's usually how a pattern gets used. How code evolves to satisfy its specifications is what determines which patterns are used; it is wrong to start out by saying which patterns will be used before you have a good sense of the high-level design.
|
|
|
|
|
Thank you, permit me to share this as a tweet.
|
|
|
|
|
|
The most correct thing previous commenters posted is that you need to handle business requirements first.
The other correct thing is that GoF patterns are mostly tactical.
Given that in my answer I'll assume is that architectural patterns are monolith, microservices, CQRS etc.
With that said I'd start from monolith application since microservices bring a lot of overhead that you might not need until you've pitched a PoC to some investors or might not need it at all if you're not going to have a team of sufficient size or your application won't endure high loads.
Then your application will evolve according to your business requirements and also to a fact that your understanding of business will evolve too.
Say you'll discover that your business problem contains of multiple subdomains. Then you can handle them via vertical slices.
Or you may need to handle spikes in application load. Then you might need to extract some microservices.
In general, my advice is to start with the simplest solution unless you're encounter requirements to act otherwise.
|
|
|
|
|
|
They're primitives; like having bones; not really something you can obsolete.
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
I guess the writer is correct in that specific scenario, but he has not addressed all the others.
|
|
|
|
|
Poorly considered, but it fits well within the current software engineering zeitgeist, so it makes sense that it would get written. That's the fancy way to say: "it's a fad, don't worry about it". And stop reading Uncle Bob.
- "it's not flexible", not if you consider adding an extra case to be a Big Deal, but the alternative is adding an entire new class.
- Even if you are of the opinion that creating new classes is somehow easier than creating new cases, then spend a little time thinking about what would happen when the function signature of that virtual function is changed, or if something about the API used by the subclasses to implement themselves changes. This has the annoying property that the fix you're about to write is highly non-local, you'd have to "hunt down" all the places that need to change - in the best case they correspond to compile errors.
- If a requirement is broken into N cases, but the code is spread out non-locally over N classes, your code does not look like the requirement which makes it harder to check whether it matches.
- "it's not SOLID", maybe, but SOLID is subjective and overrated.
- "horrific", let's not even talk about it.
- The presented "better way" presupposes that we have a convenient instance of the "class that represents a particular reason". How did we get that? Chances are that there's a switch hiding in a factory pattern or something. Moving the problem. And if there is some pattern such as
new AddressChanged().Update() , how about we don't wrap it in a class and just call a method that does that. - Article forgets to point out that in the first place the only domain that was considered is the business logic domain. Even if it makes sense there (which I don't agree with either), it won't make sense anywhere else. There's no way
Math.Min would have its if replaced by polymorphism.
|
|
|
|
|
swampwiz wrote: This article purports
First of course writers have different goals than programmers. Always keep that in mind when reading postings on the web.
From the link
"New requirements come along. Who would have thought? You were so sure nothing would happen"
Wrong, wrong, wrong.
Attempting to write code in case something might happen is a sure way to lead to code that is difficult to maintain.
If you already have requirements, even if it is just from a whiteboard, then your design and implementation should, of course, take that into account. But if you are claiming that you are writing your code to make it easy to add requirements that are unknown then you are at least ignorant of the challenges of maintaining legacy code. Especially if your claim is for something that might happen far in the future.
What actually ends up happening with that hubris is that code must be maintained for years or decades despite the fact that it serves no purpose. The complexity actually makes it harder, not easier, to add new features because it is more complex.
The best you can do now for such future possibilities is to write code that is easy to understand. And to make it clear how the code that you wrote now meets the requirements that you have now. Then that programmer 10 years from now who must add a feature that is actually needed then, will at least be able to understand what your code actually is doing and very likely needs to continue to do even with the new feature in place.
|
|
|
|
|
A keyword won't replace another.
Because of amount of 'features' available as new langages arrive.
Think from the core : Assembly ( or C ),
you can renew an adress , or the value, or call an adress, or a value,
so goto: leads to another 'adress' , It's go there/ do that ....
If statements are 'condition tests' area ,
you want to upgrade your style with a relevant idea,
throwing out the 'overload of logical' and the point is here : renew the keywords you use.
Like you'll have already your idea about the 'How To' :
one essential thing to get clues for relevance : measure / benchmarck / tests / and finally Compare your tries.
There are lots of 'equality' ,
if ( Evt 1 ){ ........... }{ .... }
so it's like :
while( Evt 1 ){
..............
}
while( ! Evt 1 ){
..............
}
will succeed same .. and commit the following .
It's about 'How many laps in loop' 'How many conditions to evaluate',
( or maybe a " loop/if handler " is required )
A code could use if() .. or other manner..
But in a very short and relevant time and process consumming ?
or for one hour over required work time ?
//////////
It's really 'one case' won't solve this questions',
benchmark will do !
benchmark will prove your choice as the best one.
There will never have deprecated keywords.
modified 23-Aug-21 21:01pm.
|
|
|
|
|
I can only suppose that your comment was intended for the original poster and not to me.
Member 14109043 wrote: There will never have deprecated keywords.
That might be specifically true for 'if' but in general that is not true.
|
|
|
|
|
I wouldn't label if statement as obsolete but I found dynamic polymorphism (hidden by the facade of SOLID mnemonic) pretty convenient thing in order for all OOD keywords to find their place. And indeed before entering the thread my guess was that author will make the case that if.. else.. violates the open-closed principle. And my guess was right. Although if I were the author I'd avoid such words as terrible, horrific etc
|
|
|
|
|
For example,
public void DoSomething(A a, B b, C c)
in which, many of properties of Object B b will be modified in DoSomething()
and the caller of DoSomething() will also use the changed Object B b
I would like to ask which of the following approaches is appropriate:
1) just use the B b after calling DoSomething()
public void DoSomething(A a, B b, C c)
2) Return the Object B to emphasize that B b being changed in DoSomething()
public B DoSomething(A a, B b, C c)
3) Or there is a different design of DoSomething() , that means the above method signature won't happan at all in OO world
|
|
|
|
|
I would go for the first choice. As long as the rest of your design is correct then other parts of the application will most likely rely on the b object being changed at this point.
|
|
|
|
|
You could make the method name more specific in this case: DoSomethingToB
It was only in wine that he laid down no limit for himself, but he did not allow himself to be confused by it.
― Confucian Analects: Rules of Confucius about his food
|
|
|
|
|
-sniff- -sniff-
I smell a homework or test question.
|
|
|
|
|
Not illegal.
Bastard Programmer from Hell
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
Thanks for all your replys,
but I cannot change the method name as DoSomethingToB
for example, the following method will calculate total price and update it to repository,
but also update the Customer c , such as
--Customer.Points
--Customer.Role
depended on the amount he bought:
public void SetTotalPrice(ShoppingCart s, Customer c, List<Product> lp)
and the caller of the method will use Customer.Points and Customer.Role to do other calculation.
Therefore, I cannot change the method name because its purpose is about total price,
the updated of Customer c is a side effect
I think, in OO design, the above SetTotalPrice() method is not appropriate, isn't it?
|
|
|
|
|
codecs1 wrote: the following method will calculate total price and update it to repository, To be strictly OO a method should only have one job. In this case to set the total price. Updating the repository should be a different method.
|
|
|
|
|
Yes, In OO, one method should only have one job,
Assume SetTotalPrice() has just one job: set the total price.
However, at the same time, it also produces some values of Customer .
I think there is no reason to calculate the Customer one more time after SetTotalPrice() called.
And the caller of SetTotalPrice() just uses the updated Customer logically
Therefore, I hope to know how to handle such circumstance in OO practice, Thanks!!
modified 3-Apr-21 23:35pm.
|
|
|
|
|
codecs1 wrote: I think there is no reason to calculate the Customer one more time
Err...yes that actually is a problem.
You are suggesting that you should program to convenience rather than to design. This it same ideology that leads programmers to start making base classes for everything for functional reasons and ignoring the "is a" design principle (and leading to highly coupled software as well.)
There are other ways to handle redundant functionality versus attempting to insert it in methods for convenience.
|
|
|
|
|
This still seems a strange approach. Generally, it is good to make methods as functional as possible - in the sense that we should generally avoid updating parameters within methods.
In this case, the process being performed by 'SetTotalPrice' doesn't meet the requirement of being an expected result. The method describes setting the price, but not the fact that the operation affects the value of the customer.
Consider moving some of the behaviour into another method or class, representing the process as a whole.
If the whole thing is to process an Order for example, so 'SetTotalPrice' calculates the total price, then the part to deal with Points and Roles should reside elsewhere, and the process of updating them should be part of, for example, the ProcessOrder method that in turn calls SetTotalPrice.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
Quote: in which, many of properties of Object B b will be modified in DoSomething()
To be honest situations like this are the reason why people start bragging that OOP is a problem and you should stick to FP. I mean it's hard to reason about what the B will after it gets processed by DoSomething . The same way what A and C will become.
This is the reason why I'm a proponent of the second option but to return the copy of the input without changing the input itself. Although it seems more functional at least it tells "hey here's new B for you but in case you'll need original values you may use them as well".
Alternatively, you may go with the first option. IMO void return type tells "be prepared that input parameters may be changed unpredictably"
|
|
|
|
|
"IMO void return type tells "be prepared that input parameters may be changed unpredictably"
For methods, I'd normally expect a method returning nothing to update the object itself, not the other parameters.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
How about two objects acting as input parameters? Which of them do you expect to be updated?
|
|
|
|
|