designing use-case realizations with gof design patterns 徐迎晓 xuyingxiao@126.com...
Post on 31-Dec-2015
275 Views
Preview:
TRANSCRIPT
DESIGNING USE-CASEREALIZATIONS WITH GoF
DESIGN PATTERNS
徐迎晓xuyingxiao@126.com
复旦大学软件学院
OUTLINE
Adapter
Factory
Singleton
Strategy
Composition
Facade
Observer
Adapter
问题:如何解决接口不兼容问题,几个类似的构件接口不同,如何为其提供稳定的接口 ?
解决:通过中间的适配器对象,将构件原有接口转换成另一个接口
support external third-party services, tax calculators
support external third-party services, credit authorization services,
support external third-party services,
inventory systems,
support external third-party services,
accounting systems,
Adapter pattern 是 GRASP building blocks 的特例It offers Protected Variations from changing external interfaces or third-party packages through the use of an Indirection object that applies interfaces and Polymorphism.
" Analysis" Discoveries During Design: Domain
Modela list of tax line items are associated with a sale, such as state tax, federal tax, and so forth
OUTLINE
Adapter
Factory
Singleton
Strategy
Composition
Facade
Observer
Factory
who creates the adapters?a domain object (such as a Register)?
如何确定创建哪种 adapter? such as TaxMaster-Adapter or GoodAsGoldTaxProAdapter?
another fundamental design principle
Design to maintain a separation of concerns.一般作为 architectural design principleone of the essential principles in software
engineering modularize or separate distinct concerns
into different areas, so that each has a cohesive purpose.
domain layer 软件对象相对强调纯应用逻辑的职责另一组软件对象负责与外部系统的连接
Context I Problem
当有特殊考虑时如何创建对象 ? ( 如创建对象的逻辑很复杂,或想将创建对象的职责分离出来以获得更高的内聚度等 )
Solution
Create a Pure Fabrication object called a Factory that handles the creation.
public class Factory{ public static Sample creator(){ .... if (which==1) return new MySample(); else if (which==2) return new HisSample(); }}
Sample s;s=Factory.creator();s.xxx();
Factory objects have several advantages: the responsibility of complex creation 分离
到高内聚的 helper objects 中将潜在的复杂的创建对象的逻辑隐藏可进而引入增强性能的内存管理策略,如
object caching or recycling.
Also is an example of a partial data-driven design通过改变属性值来选择创建不同的 adapter
对象Protected Variations : Adapter 的实现类在变化
OUTLINE
Adapter
Factory
Singleton
Strategy
Composition
Facade
Observer
Singleton (GoF)
who creates the factory itself, and how is it accessed?整个过程只需要一个 factory 实例代码中各个地方都可能需要调用 factory 中
的方法来创建实例 -- 〉 Thus, there is a visibility problem: how to get visibility to this single ServicesFactory instance?
Con text I Problem
只允许创建一个实例— it is a “singleton.”需要为对象提供全局的、单点访问
Solution
Define a static method of the class that returns the singleton.
lazy initializaition// Singleton.javapublic class Singleton{
private static Singleton _instance = null;private Singleton(){}
public static Singleton getInstance(){
if (null==_instance)_instance = new Singleton();
return _instance;}
}
保存全类唯一的实例
对客户隐藏构造器
客户只能从此处获得实例
eager initializaition
public class Singleton {
private static Singleton _instance = new Singleton();
public static Singleton getInstance() {
return _instance;
}
}
使用
UML 表示
Fig. 26.7
:Register1
:ServicesFactory
aa = getAccountingAdapterinitialize
...
the ‘1’ indicates that visibility to this instance was achieved via the Singleton pattern
Conclusion
综合使用 Adapter, Factory, and Singleton patterns ,针对外部系统 (tax calculators, accounting systems 等 ) 的各种接口变化提供了 PV
Fig. 26.8
:Register accountingAdapter : SAPAccountingAdapter
postSale ( sale )
makePayment
SOAP over HTTP
xxx
:Register
1:ServicesFactory
accountingAdapter = getAccountingAdapter
:Store
createcreate
: SAPAccountingAdapter
: Paymentcreate (cashTendered )
«actor»: SAPSystem
create
OUTLINE
Adapter
Factory
Singleton
Strategy
Composition
Facade
Observer
Strategy (GoF)
如何提供复杂的 pricing logic (圣诞促销、会员价 ,……)
The pricing strategy(a rule, policy, or algorithm) can vary during different period
Context / Problem
对于变化着的、但又相关的算法和策略如何设计 ? 如何设计才能改变这些算法和策略 ?
Solution
将每个 algorithm/policy/strategy 定义在具有共同接口的不同的类中 .
Fig. 26.10
:PercentDiscountPricingStrategy
s : Sale
st = getSubtotal
t = getTotal
lineItems[ i ] :SalesLineItem
t = getTotal( s )
pdt = getPreDiscountTotal
{ t = pdt * percentage }
note that the Sale s is passed to the Strategy so that it has parameter visibility to it for further collaboration
loop
Creating a Strategy with a Factory
Fig. 26.13
:Sale
1:PricingStrategyFactory
ps =getSalePricingStrategy
:Register
makeNewSalecreate
create(percent) ps : PercentDiscountPricingStrategy
public class Sale{ ISalePricingStrategy pricingStrategy ; double predicounttotal;
double getTotal(){...
predicounttotal=… pricingStrategy=PricingStrategyFactory.getSalePricingStrategy();
return pricingStrategy.getTotal( this ) // pricingStrategy got from PricingStrategyFactory
}}
public interface ISalePricingStrategy { public double getTotal(Sale s);}
public class PricingStrategyFactory{ public ISalePricingStrategy getSalePricingStrategy(){
//… by "salepricingstrategy.class.name" return strategy;
}}
public class PercentDiscountPricingStrategy implements ISalePricingStrategy { public double getTotal( s:Sale ){
return s.getPreDiscountTotal() * percentage }
}
Reading and Initializing the Percentage Value初始值从外部数据源如关系数据库中读取,
以便容易修改
Summary
通过 Strategy and Factory patterns 获得针对定价策略变化的 PV.
对象设计中,基于 Polymorphism and interfaces 的策略模式允许可 pluggable algorithms
策略通常通过 Factory 模式创建
Strategy 和 Factory 有一定的类似 , Strategy 相对简单容易理解 , 并且可以在运行时刻自由切换。 Factory 重点是用来创建对象
Define a family of algorithms
Encapsulate each algorithm
Make the algorithms interchangeable
Let the algorithm vary independently from clients that use it
OUTLINE
Adapter
Factory
Singleton
Strategy
Composition
Facade
Observer
Composition(GoF)
对于多个冲突的定价策略如何处理 ?高级会员打 8 折超过 XXX 元打 8.5 折星期一购买超过 XXX 元立即返还 XX 元买 1 盒龙井,则买所有商品都打 8.5 折
影响 Sale 的定价策略有三个要素
1. time period (Monday)
2. customer type (senior)
3. a particular line item product ( 买 1 盒龙井,则买所有商品都打 8.5 折 )
Context / Problem如何将组合结构和原子结构的对象进行同样
的处理Solution
Define classes for composite and atomic objects so that they implement the same interface.
Fig. 26.15
:CompositeBestForCustomerPricingStrategy
s : Sale
st = getSubtotal
t = getTotal
lineItems[ i ] :SalesLineItem
t = getTotal( s )
the Sale object treats a Composite Strategy that contains other strategies just like any other ISalePricingStrategy
x = getTotal( s )
strategies[ j ] :: ISalePricingStrategy
UML: ISalePricingStrategy is an interface, not a class; this is the way in UML 2 to indicate an object of an unknown class, but that implements this interface
{ t = min(set of all x) }
loop
loop
将对象组合成树形结构以表示“部分 -整体”的层次结构。 C o m p o s i t e 使得用户对单个对象和组合对象的使用具有一致性
Creating Multiple SalePricingStrategies
什么时候创建这些策略 ?Current store-defined discount
added when the sale is created.Customer type discount,
added when the customer type is communicated to the POS.
Product type discountadded when the product is entered to the sale.
:Sale
1:PricingStrategyFactory
ps =getSale
PricingStrategy
:Register
makeNewSale create
create ps :CompositeBestForCustomerPricingStrategy
create( percent ) s : PercentageDiscountPricingStrategy
add( s )
Customer Type DiscountUse Case : Process Sale
5b. Customer says they are eligible for a discount
1. Cashier signals discount request
2. Cashier enters Customer identification
3. System presents discount total
Introducing the new system operation,
enterCustomerForDiscount(custId)
s :Sale:Register
enterCustomerForDiscount( custID )
by Controllerby Expert and IDs to Objects
:Store
c = getCustomer( custID )
enterCustomerForDiscount( c : Customer )
by Expert
ref Enter CustomerFor Discount
s :Sale
<<singleton>>:PricingStrategy
Factory
addCustomerPricingStrategy( s )
ps :CompositeBestForCustomerPricingStrategy
ISalePricingStrategy
create( pct ) s : PercentageDiscountPricingStrategy
ISalePricingStrategy
add( s )
by Expert
enterCustomerForDiscount( c : Customer
)
originates inanother diagram
c :=getCustomer()
by Factory andHigh Cohesion
by Expert ps := getPricingStrategy()
pct :=getCustomer
Percentage( c )
by High Cohesion
by Factory and Composite
PassAggregateObject asParameter
s :Sale
1:PricingStrategy
Factory
addCustomerPricingStrategy( s )
ps :CompositeBestForCustomerPricingStrategy
create( pct ) s : PercentageDiscountPricingStrategy
add( s )
by Expert
enterCustomerForDiscount( c : Customer )
c = getCustomer
by Factory and High Cohesion
by Expert ps = getPricingStrategy
pct = getCustomer
Percentage( c )
by High Cohesion
by Factory and Composite
Pass Aggregate Object as Parameter
sd Enter Customer For Discount
OUTLINE
Adapter
Factory
Singleton
Strategy
Composition
Facade
Observer
Facade (GoF)
NextGen POS Requirementspluggable business rules.
用例场景可预测的点上(如 Process Sale 的makeNewSale, enterItem 等),不同顾客的行为略有不同
如 when a new sale is created,礼券payment type of change duescharitable donation rules
Analysis and Design the specific scenario points across all use
cases must be identified (enterItem, chooseCashChange, ...) 本次只考虑 enterltem
low impact on the existing software components
may want to experiment with different solutions
Context/Problem需要为不同的实现或子系统内部的接口提供公共的、统一的接口 .
外界对子系统内部有很多耦合,或子系统的实现可能会改变
Solution为子系统设计单点访问
– a facade object that wraps the subsystem. The facade object 代表单一的、统一的接口
Creating facade object and calling operations in facade objectFacades are often accessed via a
singleton factory. Calls to the facade should be placed near
the start of the methods that have been defined as the points for pluggable rules
public class Sale
{
public void makeLineItem (ProductSpecification spec, int quantity)
{
SalesLineItem sli = new SalesLineItem(spec, quantity);
if (POSRuleEngineFacade.getInstance().isInvalid(sli, this))
return;
.....
}
...
}
Domain
+ Sale + Register ...
POSRuleEngine
<<interface>>- IRule
...
- Rule1
...
- Rule2
...
...
package name may beshown in the tab
visibility of the package element (tooutside the package) can be shownby preceding the element name with avisibility symbol
+ POSRuleEngineFacade
instance : RuleEngineFacade
getInstance() : RuleEngineFacade
isInvalid( SalesLineItem, Sale )isInvalid( Payment, Sale )...
*1
Observer/Publish-Subscribe/Delegation
Event Model
Goal: When the total of the salechanges, refresh the display withthe new value
Sale
total...
setTotal( newTotal )...
Context/Problem不同的 subscriber 对某个 publisher 的状态变化
和事件感兴趣,当 publisher 生成事件时,每个subscriber 用各自独有的方式响应
Publisher 和 subscribers之间希望低耦合Solution
定义一个 subscriber or listener 接口Subscribers 实现这个接口 .Publisher 可动态地将对自己感兴趣的
subscribers 进行登记事件发生时, notify 所有登记的 subscribers
<<interface>>PropertyListener
onPropertyEvent( source, name, value )
SaleFrame1
onPropertyEvent( source, name, value )
initialize( Sale sale )...
javax.swing.JFrame
...setTitle()setVisible()...
{ if ( name.equals("sale.total") ) saleTextField.setText( value.toString() );}
Sale
addPropertyListener( PropertyListener lis )publishPropertyEvent( name, value )
setTotal( Money newTotal )...
*propertyListeners
{ total = newTotal; publishPropertyEvent( "sale.total", total); }
{ propertyListeners.add( lis );}
{ for each PropertyListener pl in propertyListeners pl.onPropertyEvent( this, name, value ); }
{ sale.addPropertyListener( this ) ...}
Subject
Observer
具体的Observer
update( )
update( )
Subscribing Listener Objects
s :Salesf :SaleFrame1
initialize( s : Sale)
addPropertyListener( sf)
PropertyListener
propertylisteners:Object
add( sf)
PropertyListener UML notation: Recall that
there is no such thing as aninstance of an interface.Therefore, we can generalize(or be vague) and simplyindicate the instances as typeObject, which is assumed tobe the superclass of allclasses. This approach can beused even if the language(such as C++) does notliterally define a root Objectsuperclass.
Fig. 26.23
s : Salesf : SaleFrame1
initialize( s : Sale )
addPropertyListener( sf )
propertyListeners : List<PropertyListener>
add( sf )
Publishing an event(Sale total change)
s :Sale
setTotal( total)
* : onPropertyEvent( s, "sale.total", total)
UML notation: Since themembers of this collection aretyped to the interfacePropertyListener, it is not legal toshow an implementationresponse, such as furthermessaging, since the responsecan vary, depending on the classimplementing this interface.
Therefore, "stop" the messagingat this point, and showimplementions on differentdiagrams.
publishPropertyEvent( "sale.total", total )
propertylisteners:Object
PropertyListener
Fig. 26.24
s :Sale
setTotal( total )
onPropertyEvent( s, "sale.total", total )
publishPropertyEvent( "sale.total", total )
propertylisteners[ i ] : PropertyListener
loop
Receiving event notification
:SaleFrame1
onPropertyEvent( source, name, value)
PropertyListener
saleTextField: JTextField
setText( value.toString())
Since this is a polymorphic operationimplemented by this class, show a newinteraction diagram that starts with thispolymorphic version
UML notation: Note this little expression within theparameter. This is legal and consise.
<<interface>>AlarmListener
onAlarmEvent( source, time )
Beeper
onAlarmEvent( source, time )...
{ display notification dialogbox}
AlarmClock
addAlarmnListener( AlarmListener lis )publishAlarmEvent( time )
setTime( newTime )...
*alarmListeners
{ time = newTime; if ( time == alarmTime ) publishAlarmEvent( time ); }
{ alarmListeners.add( lis );}
{ for each AlarmListener al in alarmListeners al.onAlarmEvent( this, time ); }
AlarmWindow
onAlarmEvent( source, time )...
javax.swing.JFrame
...setTitle()setVisible()...
ReliabilityWatchDog
onAlarmEvent( source, time )...
{ beep}
{ check that all required processes are executing normally}
补充内容
OUTLINE
Adapter
Factory
Singleton
Strategy
Composition
Facade
Observer
More on Factory and Singleton
xuyingxiao@126.com
Existing class
class Employee{ //... public void youAreFired() { // lots of code }}a lot of code scattered through the system that looks something like this Employee fred = new Employee(); //... fred.youAreFired();
must-change-all-new-invocations problem
1 Create an interface that has the same name as the existing class, like so:interface Employee{ void youAreFired();}2 Rename the existing class, like so:class Peon implements Employee // used to be Employee{ public void youAreFired() { // lots of code }}3. ?
Factory 1 public interface Employee 2 { void youAreFired(); 3 } 4 5 public static class EmployeeFactory 6 { private Factory(){/*empty*/} 7 8 public static Employee create() 9 { return new Peon();10 }11 }
/*package*/ class Peon implements Employee14 { public void youAreFired()15 { // lots of code16 }17 }
restrict access to the concrete class even further public static class EmployeeFactory{ private EmployeeFactory(){/*empty*/} public static Employee create() { return new Peon(); } private static class Peon implements Employee { public void youAreFired() { // lots of code } }}
anonymous-inner-class version of the factory
public static class EmployeeFactory{ private Factory(){/*empty*/} public static Employee create() { return new Employee() { public void youAreFired() { // lots of code } } }}
Runtime runtimeEnvironment = Runtime.getRuntime();
ButtonPeer peer = Toolkit.getDefaultToolkit().createButton(b);
Thread Problem
1 class Singleton 2 { private static Singleton instance = null; 3 public static instance() 4 { if( instance == null ) 5 { instance = new Singleton(); 6 } 7 return instance; 8 } 9 //...10 }
The easy solution is to use a static initializer
class Singleton2
{ private static Singleton instance = new Singleton();
public instance() { return instance; }
//...
}
The only time the static -initializer approach isn't workable
public static void main( String[] args ){ //... Connection.pointAt( new URL(args[i]) ); //...}
Connection c = Connection.instance();
class Connection{ private static URL server; public static pointAt( URL server ){ this.server = server; } private Connection() { //... establishConnectionTo( server ); //... } private static Connection instance; public synchronized static Connection instance() { if( instance == null ) instance = new Connection(); return connection(); }}
Double-Checked Locking 1 class DoubleChecked 2 { private static volatile DoubleChecked instance = null; 3 private static Object lock = new Object(); 4 5 public static DoubleChecked instance() 6 { if( instance == null ) 7 { synchronized( lock ) 8 { if( instance == null ) 9 instance = new DoubleChecked();10 }11 }12 return instance;13 }14 }
Game of Life
Game of Life
对于处在”生态的格 ,若八个邻居中有 2 个或 3 个”生” ,则继续存活 ,否则将因过于孤独或过于拥挤而死亡 .
对于处在”死”态的空格 ,若八个邻格中有 3 个”生” , 则该格转变为”生” ( 代表繁衍过程 ),否则继续空着 .
Observer Pattern
ObserverObserver uses an anonymous inner class as the Concrete Subject/Subscriber
class Client{ volatile boolean menuItemSelected = false;
public Client( JMenu topMenu ) { // Add an item to the topMenu, and arrange to be notified when it // is selected:
JMenuItem myItem = new JMenuItem( "Hello" ); myItem.addActionListener ( new ActionListener() { public void actionPerformed( ActionEvent e ) { menuItemSelected = true; // process selection } } );
topMenu.add( myItem ); }}
Universe——Observerclass Universe{…Clock.instance().addClockListener( new Clock.Listener() { public void tick() { // code to handle a clock tick goes here } });
}
Clock——Timer 13 public class Clock{ 14 private Timer clock = new Timer(); 15 private TimerTask tick = null; 16
21 private Clock() 22 { createMenus(); 23 }
39 public void startTicking( int millisecondsBetweenTicks ) 40 { if(tick != null) 41 { tick.cancel(); 42 tick=null; 43 } 44 45 if( millisecondsBetweenTicks > 0 ) 46 { tick = new TimerTask() 47 { public void run(){ tick(); } 48 }; 49 clock.scheduleAtFixedRate( tick, 0, millisecondsBetweenTicks); 50 } 51 }
97 public void tick(){ …
Implementing Observer: The Publisher Classclass Publisher1
{ ArrayList subscribers = new ArrayList();
public synchronized void subscribe( Runnable subscriber ) { subscribers.add( subscriber ); }
public synchronized void cancelSubscription( Runnable subscriber ) { subscribers.remove( subscriber); }
private synchronized void fireEvent() // notify all subscribers { for( int i = 0; i < subscribers.size(); ++i ) ((Runnable) subscribers.get(i) ).run(); }}
top related