Design patterns to avoid [closed]

2019-01-09 21:05发布

A lot of people seem to agree, that the Singleton pattern has a number of drawbacks and some even suggest avoiding the pattern entirely. There's an excellent discussion here. Please direct any comments about the Singleton pattern to that question.

My question: Are there other design patterns, that should be avoided or used with great care?

12条回答
趁早两清
2楼-- · 2019-01-09 21:31

I think that Active Record is an overused pattern that encourages mixing business logic with the persistance code. It doesn't do a very good job of hiding the storage implementation from the model layer and ties the models to a database. There are plenty of alternatives (described in PoEAA) such as Table Data Gateway, Row Data Gateway and Data Mapper which often provide a better solution and certainly help to provide a better abstraction to storage. Also, your model should not need to be stored in a database; what about storing them as XML or accessing them using web services? How easy would it be to change the storage mechanism of your models?

That said, Active Record is not always bad and is perfect for simpler applications where the other options would be overkill.

查看更多
Evening l夕情丶
3楼-- · 2019-01-09 21:31

I hope I won't get beaten too much for this. Christer Ericsson wrote two articles (one, two) on the topic of design patterns in his real time collision detection blog. His tone is rather harsh, and perhaps a bit provocative, but the man knows his stuff, so I wouldn't dismiss it as ravings of a lunatic.

查看更多
不美不萌又怎样
4楼-- · 2019-01-09 21:32

Patterns are complex

All design patterns should be used with care. In my opinion you should refactor towards patterns when there is a valid reason to do so instead of implementing a pattern right away. The general problem with using patterns is that they add complexity. Overuse of patterns makes a given application or system cumbersome to further develop and maintain.

Most of the time, there is a simple solution, and you won't need to apply any specific pattern. A good rule of thumb is to use a pattern whenever pieces of code tend to be replaced or need to be changed often and be prepared to take on the caveat of complex code when using a pattern.

Remember that your goal should be simplicity and employ a pattern if you see a practical need to support change in your code.

Principles over patterns

It may seem like a moot to use patterns if they can evidently lead to over-engineered and complex solutions. However it is instead much more interesting for a programmer to read up on design techniques and principles that lay the foundation for most of the patterns. In fact one of my favorite books on 'design patterns' stresses this by reiterating on what principles are applicable on the pattern in question. They are simple enough to be useful than patterns in terms of relevance. Some of the principles are general enough to encompass more than object oriented programming (OOP), such as Liskov Substitution Principle, as long as you can build modules of your code.

There are a multitude of design principles but those described in the first chapter of GoF book are quite useful to start with.

  • Program to an 'interface', not an 'implementation'. (Gang of Four 1995:18)
  • Favor 'object composition' over 'class inheritance'. (Gang of Four 1995:20)

Let those sink in on you for a while. It should be noted that when GoF was written an interface means anything that is an abstraction (which also means super classes), not to be confused with the interface as a type in Java or C#. The second principle comes from the observed overuse of inheritance which is sadly still common today.

From there you can read up on SOLID principles which was made known by Robert Cecil Martin (aka. Uncle Bob). Scott Hanselman interviewed Uncle Bob in a podcast about these principles:

  • Single Responsibility Principle
  • Open Closed Principle
  • Liskov Substitution Principle
  • Interface Segregation Principle
  • Dependency Inversion Principle

These principles are a good start to read up on and discuss with your peers. You may find that the principles interweave with each other and with other processes such as separation of concerns and dependency injection. After doing TDD for a while you also may find that these principles come naturally in practice as you need to follow them to some degree in order to create isolated and repeatable unit tests.

查看更多
够拽才男人
5楼-- · 2019-01-09 21:32

Singletons - a class using singleton X has a dependency on it that's hard to see and hard to isolate for testing.

They're used very often because they're convenient and easy to understand, but they can really complicate testing.

See Singletons are Pathological Liars.

查看更多
混吃等死
6楼-- · 2019-01-09 21:41

I believe the observer pattern has a lot to answer for, it works in very general cases, but as systems become more complex it becomes a nightmare, needing OnBefore(), OnAfter() notifications, and often posting of asynchronous tasks to avoid re-entrancy. A much better solution is to develop a system of automatic dependency analysis that instruments all object accesses (with read-barriers) during calculations and automatically creates an edge in a dependency graph.

查看更多
来,给爷笑一个
7楼-- · 2019-01-09 21:42

The one that the authors of Design Patterns themselves most worried about was the "Visitor" pattern.

It's a "necessary evil" - but is often over used and the need for it often reveals a more fundamental flaw in your design.

An alternative name for the "Visitor" pattern is "Multi-dispatch", because the Visitor pattern is what you end up with when you wish to use a single-type dispatch OO language to select the code to use based on the type of two (or more) different objects.

The classic example being that you have the intersection between two shapes, but there's an even simpler case that's often overlooked: comparing the equality of two heterogeneous objects.

Anyway, often you end up with something like this:

interface IShape
{
    double intersectWith(Triangle t);
    double intersectWith(Rectangle r);
    double intersectWith(Circle c);
}

The problem with this is that you have coupled together all of your implementations of "IShape". You've implied that whenever you wish to add a new shape to the hierarchy you will need to change all the other "Shape" implementations too.

Sometimes, this is the correct minimal design - but think it through. Does your design really mandate that you need to dispatch on two types? Are you willing to write each of the combinatorial explosion of multi-methods?

Often, by introducing another concept you can reduce the number of combinations that you're actually going to have to write:

interface IShape
{
    Area getArea();
}

class Area
{
    public double intersectWith(Area otherArea);
    ...
}

Of course, it depends - sometimes you really do need to write code to handle all of those different cases - but it's worth taking pause and having a think before taking the plunge and using Visitor. It might save you a lot of pain later on.

查看更多
登录 后发表回答