Skip to content

Commit 101ca01

Browse files
m.lm.l
m.l
authored and
m.l
committed
添加了原文
1 parent bb18718 commit 101ca01

File tree

2 files changed

+113
-21
lines changed

2 files changed

+113
-21
lines changed

第五章-装饰器模式.md

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@ Whenever we want to add extra functionality to an object, we have a number of di
55

66
任何时候,只要我们想给一个对象添加附加的功能,就有多个不同的选项供我们选择。我们可以选择:
77

8-
```
9-
Add the functionality directly to the class the object belongs to, if it makes sense (for example, add a new method)
10-
Use composition
11-
Use inheritance
12-
```
138

14-
```
15-
直接对类的对象添加功能,如果可行的话(例如,添加一个新方法)
16-
合成
17-
继承
18-
```
9+
- Add the functionality directly to the class the object belongs to, if it makes sense (for example, add a new method)
10+
- Use composition
11+
- Use inheritance
12+
13+
14+
- 直接对类的对象添加功能,如果可行的话(例如,添加一个新方法)
15+
- 合成
16+
- 继承
17+
1918

2019
Composition should generally be preferred over inheritance, because inheritance makes code reuse harder, it's static, and applies to an entire class and all instances of it [GOF95, page 31], [j.mp/decopat].
2120

@@ -43,25 +42,25 @@ Python的装饰器和装饰器模式之间不存在一对一的关系。Python
4342
## 软件的例子
4443
Django框架使用装饰器来实现良好的扩展。例子就是视图装饰器。Django的`视图`装饰器可以用于:
4544

46-
```
47-
限制基于HTTP请求的视图
4845

49-
控制特定视图的缓存行为
46+
- 限制基于HTTP请求的视图
5047

51-
控制每个视图基础的压缩
48+
- 控制特定视图的缓存行为
49+
50+
- 控制每个视图基础的压缩
51+
52+
- 控制依据特定HTTP请求偷渡的缓存
5253

53-
控制依据特定HTTP请求偷渡的缓存
54-
```
5554

5655
Grok框架也使用装饰器来实现不同的目标:
5756

58-
```
59-
函数注册为一个时间订阅器
6057

61-
对方法应用一个特定的权限
58+
- 函数注册为一个时间订阅器
59+
60+
- 对方法应用一个特定的权限
61+
62+
- 实现适配器模式
6263

63-
实现适配器模式
64-
```
6564

6665
## 使用案例
6766
装饰器模式用于实现`横切关注点`时特别出色。横切关注点的例子有:
@@ -93,3 +92,5 @@ Python装饰器是通用的并且非常强大。你可以发现很多的
9392

9493
## 总结
9594
本章内容覆盖了装饰器模式以及它与Python编程语言的关系。我们使用装饰器模式来作为扩展一个对象的行为的很方便的方式,而不使用继承。Python扩展
95+
96+

第十三章-观察者模式.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,92 @@
1+
第十三章-观察者模式 The Observer Pattern
2+
**************************************
3+
Sometimes, we want to update a group of objects when the state of anotherobject changes. A very popular example lies in the Model-View-Controller (MVC) pattern. Assume that we are using the data of the same model in two views, for instance in a pie chart and in a spreadsheet. Whenever the model is modified,both the views need to be updated. That's the role of the Observer design pattern [Eckel08, page 213].
4+
The Observer pattern describes a publish-subscribe relationship between a single object, : the publisher, which is also known as the subject or observable, and one or more objects, : the subscribers, also known as observers. In the MVC example, the publisher is the model and the subscribers are the views. However, MVC is not the only publish-subscribe example. Subscribing to a news feed such as RSS or Atom is another example. Many readers can subscribe to the feed typically using a feed reader, and every time a new item is added, they receive the update automatically.
5+
The ideas behind Observer are the same as the ideas behind MVC and the separation of concerns principle, that is, to increase decoupling between the publisher and subscribers, and to make it easy to add/remove subscribers at runtime. Additionally, the publisher is not concerned about who its observers are. It just sends notifications to all the subscribers [GOF95, page 327].
6+
7+
## A real-life exampleIn reality, an auction resembles Observer. Every auction bidder has a number paddle that is raised whenever they want to place a bid. Whenever the paddle is raisedby a bidder, the auctioneer acts as the subject by updating the price of the bid and broadcasting the new price to all bidders (subscribers).
8+
9+
The following figure, courtesy of www.sourcemaking.com, [j.mp/observerpat], shows how the Observer pattern relates to an auction:
10+
11+
图片:略
12+
## A software exampleThe django-observer package [j.mp/django-obs] is a third-party Django package that can be used to register callback functions that are executed when there are changes in several Django fields. Many different types of fields are supported (CharField, IntegerField, and so forth).
13+
RabbitMQ is a library that can be used to add asynchronous messaging support to an application. Several messaging protocols are supported, such as HTTP and AMQP. RabbitMQ can be used in a Python application to implement a publish-subscribe pattern, which is nothing more than the Observer design pattern [j.mp/rabbitmqobs].
14+
15+
## Use cases
16+
We generally use the Observer pattern when we want to inform/update one or more objects (observers/subscribers) about a change that happened to another object (subject/publisher/observable). The number of observers as well as who the observers are may vary and can be changed dynamically (at runtime).
17+
18+
We can think of many cases where Observer can be useful. One such case was already mentioned at the start of this chapter: news feeds. Whether it is RSS, Atom, or another format, the idea is the same; you follow a feed, and every time it is updated, you receive a notification about the update [Zlobin13, page 60].
19+
The same concept exists in social networking. If you are connected to another person using a social networking service, and your connection updates something, you are notified about it. It doesn't matter if the connection is a Twitter user that you follow, a real friend on Facebook, or a business colleague on LinkedIn.
20+
21+
Event-driven systems are another example where Observer can be (and usually is) used. In such systems, listeners are used to "listen" for specific events. The listeners are triggered when an event they are listening to is created. This can be typing a specific key (of the keyboard), moving the mouse, and more. The event plays the role of the publisher and the listeners play the role of the observers. The key point in this case is that multiple listeners (observers) can be attached to a single event (publisher) [j.mp/magobs].
22+
23+
## ImplementationIn this section, we will implement a data formatter. The ideas described here are based on the ActiveState Python Observer code recipe [j.mp/pythonobs]. There is a default formatter that shows a value in the decimal format. However, wecan add/register more formatters. In this example, we will add a hex and binary formatter. Every time the value of the default formatter is updated, the registered formatters are notified and take action. In this case, the action is to show the new value in the relevant format.
24+
25+
Observer is actually one of the patterns where inheritance makes sense. We can have a base *Publisher* class that contains the common functionality of adding, removing, and notifying observers. Our DefaultFormatter class derives from Publisherand adds the formatter-specific functionality. We can dynamically add and remove observers on demand. The following class diagram shows an instance of the example using two observers: *HexFormatter* and BinaryFormatter. Note that, because class diagrams are static, they cannot show the whole lifetime of a system, only the state of it at a specific point in time.
26+
27+
图片:略
28+
29+
We begin with the Publisher class. The observers are kept in the observers list. The `add()` method registers a new observer, or throws an error if it already exists. The `remove()` method unregisters an existing observer, or throws an exception if it does not exist. Finally, the `notify()` method informs all observers about a change:
30+
31+
```python class Publisher: def __init__(self): self.observers = [] def add(self, observer): if observer not in self.observers: self.observers.append(observer) else: print('Failed to add: {}'.format(observer)) def remove(self, observer): try: self.observers.remove(observer) except ValueError: print('Failed to remove: {}'.format(observer)) def notify(self): [o.notify(self) for o in self.observers]```
32+
33+
Let's continue with the DefaultFormatter class. The first thing that `__init__()` does is call `__init__()` method of the base class, since this is not done automatically in Python. A DefaultFormatter instance has name to make it easier for us to track its status. We use name mangling in the _data variable to state that it should not be accessed directly. Note that this is always possible in Python [Lott14, page 54] but fellow developers have no excuse for doing so, since the code already states that they shouldn't. There is a serious reason for using name mangling in this case. Stay tuned. DefaultFormatter treats the _data variable as an integer, and the default valueis zero:
34+
35+
```python class DefaultFormatter(Publisher): def __init__(self, name): Publisher.__init__(self) self.name = name self._data = 0
36+
```
37+
38+
39+
The `__str__()` method returns information about the name of the publisher and the value of `_data. type(self).__name__` is a handy trick to get the name of a class without hardcoding it. It is one of those things that make the code less readable but easier to maintain. It is up to you to decide if you like it or not:
40+
41+
```python def __str__(self): return "{}: '{}' has data = {}".format(type(self).__name__,self.name,self._data)
42+
```
43+
There are two data() methods. The first one uses the @property decorator to give read access to the` _data variable`. Using this, we can just execute object.data instead of object.data():
44+
45+
```python @property def data(self): return self._data
46+
```
47+
The second data() method is more interesting. It uses the @setter decorator, which is called every time the assignment (=) operator is used to assign a new value to the _data variable. This method also tries to cast a new value to an integer, and does exception handling in case this operation fails:
48+
49+
```python @data.setter def data(self, new_value): try: self._data = int(new_value) except ValueError as e: print('Error: {}'.format(e)) else: self.notify()
50+
```
51+
52+
The next step is to add the observers. The functionality of HexFormatter and BinaryFormatter is very similar. The only difference between them is how they format the value of data received by the publisher, that is, in hexadecimal and binary, respectively:
53+
54+
```python class HexFormatter: def notify(self, publisher): print("{}: '{}' has now hex data = {}".format(type(self).__ name__, publisher.name, hex(publisher.data))) class BinaryFormatter: def notify(self, publisher): print("{}: '{}' has now bin data = {}".format(type(self).__ name__, publisher.name, bin(publisher.data)))
55+
```
56+
57+
No example is fun without some test data. The main() function initially creates a DefaultFormatter instance named test1 and afterwards attaches (and detaches) the two available observers. Exception handling is also exercised to make sure that the application does not crash when erroneous data is passed by the user. Moreover, things such as trying to add the same observer twice or removing an observer that does not exist should cause no crashes:
58+
59+
```python def main(): df = DefaultFormatter('test1') print(df) print() hf = HexFormatter() df.add(hf) df.data = 3 print(df) print() bf = BinaryFormatter() df.add(bf) df.data = 21 print(df)
60+
print() df.remove(hf) df.data = 40 print(df) print() df.remove(hf) df.add(bf) df.data = 'hello' print(df) print() df.data = 15.8 print(df)
61+
```
62+
63+
Here's how the full code of the example (`observer.py`) looks:
64+
65+
```python class Publisher: def __init__(self): self.observers = [] def add(self, observer): if observer not in self.observers: self.observers.append(observer) else: print('Failed to add: {}'.format(observer)) def remove(self, observer): try: self.observers.remove(observer) except ValueError: print('Failed to remove: {}'.format(observer)) def notify(self): [o.notify(self) for o in self.observers] class DefaultFormatter(Publisher): def __init__(self, name): Publisher.__init__(self) self.name = name self._data = 0
66+
67+
def __str__(self): return "{}: '{}' has data = {}".format(type(self).__name__, self.name, self._data) @property def data(self): return self._data @data.setter def data(self, new_value): try: self._data = int(new_value) except ValueError as e: print('Error: {}'.format(e)) else: self.notify() class HexFormatter: def notify(self, publisher): print("{}: '{}' has now hex data = {}".format(type(self).__ name__, publisher.name, hex(publisher.data))) class BinaryFormatter: def notify(self, publisher): print("{}: '{}' has now bin data = {}".format(type(self).__ name__, publisher.name, bin(publisher.data))) def main(): df = DefaultFormatter('test1') print(df)print()hf = HexFormatter()df.add(hf)df.data = 3print(df)print()bf = BinaryFormatter()df.add(bf)df.data = 21print(df)print()df.remove(hf)df.data = 40print(df)
68+
print() df.remove(hf) df.add(bf) df.data = 'hello' print(df) print() df.data = 15.8 print(df) if __name__ == '__main__': main()
69+
```
70+
71+
Executing observer.py gives the following output:
72+
73+
```python
74+
>>> python3 observer.pyDefaultFormatter: 'test1' has data = 0HexFormatter: 'test1' has now hex data = 0x3DefaultFormatter: 'test1' has data = 3
75+
HexFormatter: 'test1' has now hex data = 0x15BinaryFormatter: 'test1' has now bin data = 0b10101DefaultFormatter: 'test1' has data = 21BinaryFormatter: 'test1' has now bin data = 0b101000DefaultFormatter: 'test1' has data = 40
76+
Failed to remove: <__main__.HexFormatter object at 0x7f30a2fb82e8>Failed to add: <__main__.BinaryFormatter object at 0x7f30a2fb8320>Error: invalid literal for int() with base 10: 'hello'BinaryFormatter: 'test1' has now bin data = 0b101000DefaultFormatter: 'test1' has data = 40BinaryFormatter: 'test1' has now bin data = 0b1111DefaultFormatter: 'test1' has data = 15
77+
```
78+
What we see in the output is that as the extra observers are added, more (and relevant) output is shown, and when an observer is removed, it is not notified any longer. That's exactly what we want: runtime notifications that we are able to enable/disable on demand.
79+
The defensive programming part of the application also seems to work fine. Trying to do funny things such as removing an observer that does not exist or adding the same observer twice is not allowed. The messages shown are not very user-friendly but I leave that up to you as an exercise. Runtime failures of trying to pass a string when the API expects a number are also properly handled without causing the application to crash/terminate.
80+
This example would be much more interesting if it were interactive. Even a simple menu that allows the user to attach/detach observers at runtime and modify the value of DefaultFormatter would be nice because the runtime aspect becomes much more visible. Feel free to do it.
81+
Another nice exercise is to add more observers. For example, you can add an octal formatter, a roman numeral formatter, or any other observer that uses your favorite representation. Be creative and have fun!
82+
83+
## SummaryIn this chapter, we covered the Observer design pattern. We use Observer when we want to be able to inform/notify all stakeholders (an object or a group of objects) when the state of an object changes. An important feature of observer is that the number of subscribers/observers as well as who the subscribers are may varyand can be changed at runtime.
84+
To understand Observer, you can think of an auction, with the bidders being the subscribers and the auctioneer being the publisher. This pattern is used quite a lot in the software world.
85+
In general, all systems that make use of the MVC pattern are event-based. As specific examples, we mentioned:
86+
• django-observer, a third-party Django library used to register observers that are executed when fields are modified.
87+
• The Python bindings of RabbitMQ. We referred to a specific example of RabbitMQ used to implement the publish-subscribe (aka Observer) pattern.
88+
In the implementation example, we saw how to use Observer to create data formatters that can be attached and detached at runtime to enrich the behavior of an object. Hopefully, you will find the recommended exercises interesting.
89+
The next chapter introduces the State design pattern, which can be used to implement a core computer science concept: state machines.
90+
91+
192

0 commit comments

Comments
 (0)