UML 2 ObjectOriented Analysis Design with UML 2
UML 2面向对象分析与设计 Object-Oriented Analysis & Design with UML 2 谭火彬
第 07章 面向对象的设计模式 Object-Oriented Design Patterns
模式 þ 来自建筑行业 u Christopher Alexander u 1977年《A Pattern Language》 u Each pattern describes a problem which occurs over and over again in our environments, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice. þ 通用问题的解决方案 -9 -
模式的三大要素 þ C. Alexander: Each pattern is a three-part rule, which expresses a relation between a certain context, context a problem, problem and a solution u 背景(context):那些适合运用该模式的 可重现情形 u 问题(problem):出现在背景中的一系列 影响力,即目标与约束 u 解决方案(solution):可用于解决问题的 经典设计形式或设计规则 -10 -
模式简史-1 1964 Christopher Alexander出版了Notes on the Synthesis of Form ,该书尝试从一个不同的角度来看待建筑的过程 1977 Christopher Alexander出版了A Pattern Language一书 1987 Ward Cunningham和Kent Beck开始将一些建筑学概念应用到使 用Small. Talk语言的软件开发中 1992 Jim Coplien出版了Advanced C++:Programming Style and Idioms一书,将模式引入到程序设计领域 1992 Peter Coad在ACM上发表了他在分析模式方面的研究成果 1993 Erich Gamma的博士论文以及John. Vlissides、Ralph Johnson和 Richard Helm的其它研究成果在ECOOP 93上发表 1993 Kent Beck、Grady Booch、Jim Coplien以及其他人组成了 Hillside小组,提供一个讨论模式的论坛 -13 -
模式简史-2 1994 第一次编程模式语言(Pattern Language of Programming, PLo. P)大会举行 1994 Design Patterns: Elements of Reusable Object-Oriented Software出版,使模式在设计领域得到广泛应用 1996 Frank Buschmann与其他人合作出版了Pattern-Oriented Software Architecture: A System of Patterns一书 1997 Martin Flower出版了Analysis Patterns: Reusable Object Models一书,将模式引入分析领域 1999 Martin Flower与其他人合作出版了Refactoring:Improving the Design of Existing Code一处 …… -14 -
Go. F设计模式 þ 时间 u 1994 þ 人物 u Erich Gamma、John Vlissides、Ralph Johnson、Richard Helm u 合称Go. F (Gang of Four) þ 事件 u Design Patterns: Elements of Reusable Object-Oriented Software (设计模式-可 复用面向对象软件的基础) 的出版 u 提出 23种设计模式,统称为Go. F模式 -23 -
Go. F设计模式 Go. F 23种设计模式 创建型 类 Factory Method 对象 Abstract Factory Builder Prototype Singleton 结构型 行为型 Adapter (Class) Interpreter Template Method Adapter (Object) Bridge Composite Decorator Facade Flyweight Proxy Chain of Responsibility Command Iterator Mediator Memento Observer State Strategy Visitor -26 -
Go. F应用-State模式-1 Lego. System: : Process. Color(){ switch (color) { case RED: Red. Process(); break; case GREEN: Green. Process(); //修改Lego. System源代码 break; case BLUE: case YELLOW: Blue. Process(); Yellow. Process(); break; } }; -33 -
State模式-3 -35 -
State模式-4 Lego. System: : Process. Color(){ Color->Process(); }; CColor{ public: virtual void Process(); } State模式满足OCP void CRed: : Process(){ Red. Process(); }; void CGreen: : Process(){ //仅需要增加新的类, Green. Process(); //原有代码不需要任何变动 }; void CYellow: : Process(){ void CBlue: : Process(){ Blue. Process(); Yellow. Process(); }; }; -36 -
汉堡店:Decorator模式 -42 -
应用Decorator模式 -43 -
可复用的Button 13681312345 Button key. Dial = dial. Key. Singleton(); Button key. Power = power. Key. Singleton(); Button key 9 = new Button(“ 9”); -47 -
根据类图试写Button代码 public class Button { private Dialer my. Dialer; public Button(Dialer dialer) { my. Dialer = dialer; } public button. Pressed(String token) throws Number. Format. Exception { int digit = Integer. parse. Int(token); dialer. digit(digit); } } Button与Dialer之间出现耦合,从而破坏了Button的可复用性 我们考虑增加一个间接层来解决? -50 -
新的问题? interface Button. Listener { public void button. Pressed(int token) } public class Dialer implements Button. Listener { public void button. Pressed(int token) … } 新问题:Dialer需要实现Button. Listener接口,即要 实现button. Pressed事件?并要求Dialer能够接受 int型的数值并组装为电话号码进行拨号?如果Dialer 是已经做好的配件,只接受一个字符串表示的电话号 码,又该怎么办?这种设计限制了Dialer的可复用性 -53 -
代码实现 interface Button. Listener { void button. Pressed(int digit); } public class Dialer { public void dial(String pno) { radio. connect(pno); } } public class Button. Dialer. Adapter implements Button. Listener { String. Buffer my. Pho = “”; Dialer my. Dialer; … void button. Pressed(int digit) { if (digit == DIAL_SIGNAL) my. Dialer. dial(my. Pho); else my. Pho. append(digit); } } -56 -
实现Observer public class Button { private int my. Digit = INVALID_DIGIT; private Linked. List observers = null; boolean add. Observer(Button. Listener bl) { return observers. add(bl); } void pressed() { List. Iterator li = observers. list. Iterator(); while (li. has. Next()) { Button. Listener bl = (Button. Listener)li. next(); bl. button. Pressed(my. Digit); } } } -60 -
运用Command模式的Button interface Command { public void do(int signal); } public class Button { Command cmd = null; int digit = 0; public Button(Command cmd) { this. cmd = cmd; } public Command set. Command(Command cmd) { Command old = this. cmd; this. cmd = cmd; return old; } public final void pressed() { cmd. do(digit); } } -63 -
更多的模式… þ Null Object þ Abstract Server þ Active Object þ …… -64 -
NULL Object模式 Employee e=DB. get. Employee(“Bob”); if (e!=null && e. is. Time. To. Pay(today)) e. pay(); Employee e=DB. get. Employee(“Bob”); if (e. is. Time. To. Pay(today)) e. pay(); -65 -
9种GRASP模式 þ Craig Larman, Applying UML and Patterns (2 nd) u 信息专家(Information Expert) u 创建者(Creator) Cohesion) u 低耦合(Low Coupling) u 控制者(Controller) u 多态( Polymorphism) u 纯虚构( Pure Fabrication) u 中介者(Indirection) u 受保护变化模式(Protected Variations) u 高内聚(High -70 -
示例:不要和陌生人讲话 class Application{ private Form form; public void execute(){ form. get. Panel(). get. Button(). do. Something(); //. . . } -74 -
Go. F:Observer模式的实现 -77 -
重构实践:影片出租店 public String statement() {…… public String statement() { double total. Amount = 0; switch(each. get. Movie(). get. Price. Code()){ switch int frequent. Renter. Points = 0; Enumeration rentals = _rentals. elements(); case Movie. REGULAR: Movie. REGULAR String result = "Rental Record for " + get. Name() + "n"; while(rentals. has. More. Elements()){ this. Amount += 2; double this. Amount = 0; Rental each = (Rental) rentals. next. Element(); if(each. get. Days. Rented()>2) //determine amounts for each line switch(each. get. Movie(). get. Price. Code()){ this. Amount += (each. get. Days. Rented()-2)*1. 5; case Movie. REGULAR: this. Amount += 2; break; if(each. get. Days. Rented()>2) this. Amount += (each. get. Days. Rented()-2)*1. 5; case Movie. NEW_RELEASE: Movie. NEW_RELEASE break; case Movie. NEW_RELEASE: this. Amount += each. get. Days. Rented()*3; break; case Movie. CHILDRENS: this. Amount += 1. 5; case Movie. CHILDRENS: Movie. CHILDRENS if(each. get. Days. Rented()>3) this. Amount += (each. get. Days. Rented()-3)*1. 5; this. Amount += 1. 5; break; } if(each. get. Days. Rented()>3) // add frequent renter points frequent. Renter. Points ++; this. Amount += (each. get. Days. Rented()-3)*1. 5; // add bonus for a two day new release rental if ((each. get. Movie(). get. Price. Code() == Movie. NEW_RELEASE) && break; each. get. Days. Rented() > 1) frequent. Renter. Points ++; } // show figures for this rental� …… result += "t" + each. get. Movie(). get. Title() + "t" + String. value. Of(this. Amount) + "n"; if ((each. get. Movie(). get. Price. Code() == Movie. NEW_RELEASE) && total. Amount += this. Amount; } each. get. Days. Rented() > 1) // add footer lines� result += "Amount owed is " + String. value. Of(total. Amount) + "n"; frequent. Renter. Points ++; result += "You earned " + String. value. Of(frequent. Renter. Points) + " frequent renter points"; ……} return result; 影片出租店管理程序:计算每一位顾客的消费金 额并打印报表(statement)。操作人员告诉程 序:顾客租了那些影片、租期多长,程序便根据 租赁时间和影片类型计算费用。影片分为三类: 普通片、儿童片和新片。除了计算费用外,还要 为常客计算点数;点数会随着“租片种类是否为 新片”而有所不同! } -82 -
- Slides: 84