FreeOZ论坛

标题: 我的实用设计模式之四-Simple Factory,Factory Method和Abstract Factory [打印本页]

作者: procoder    时间: 24-4-2009 21:48
标题: 我的实用设计模式之四-Simple Factory,Factory Method和Abstract Factory
这是我自己的一些想法,论坛里有好多高手,请指教。
Simple Factory
先从Simple Factory开始讲起,假设模拟一个电玩店的试玩系统,这个电玩店专卖出售PS3的游戏机和提供试玩服务,当一个用户想试玩的时候,需要选择一种游戏类型进行试玩,系统会选择生成其中一个游戏盘的对象:竞赛游戏(PS3RacingGameDisk),射击游戏(PS3ShootingGameDisk)以及格斗游戏(PS3FightingGameDisk),这些游戏盘子类都分别继承自同一个游戏盘抽象类AbstractGameDisk。

                               
登录/注册后可看大图

图1

public
abstract
class AbstractGameDisk
    {
        
public
string Name { get; set; }
        
public
abstract
void Load();
    }

   
public
class PS3RacingGameDisk : AbstractGameDisk
    {
        
public
override
void Load()
        {
            Console.WriteLine(
"Load PS3 racing game.");
        }
    }

   
public
class PS3ShootingGameDisk : AbstractGameDisk
    {
        
public
override
void Load()
        {
            Console.WriteLine(
"Load PS3 shooting game.");
        }
    }

   
public
class PS3FightingGameDisk : AbstractGameDisk
    {
        
public
override
void Load()
        {
            Console.WriteLine(
"Load PS3 fighting game.");
        }
    }

   
public
enum GameDiskType
    {
        RACING,
        SHOOTING,
        FIGHTING
    }

   
public
class PS3Player
    {
        
public
void PlayAGame(GameDiskType type)
        {
            
//Get a console

            
//Get a joystick

            
//create a game disk
            AbstractGameDisk disk;
            
switch (type)
            {
               
case GameDiskType.RACING:
                    disk
=
new PS3RacingGameDisk();
                    
break;
               
case GameDiskType.SHOOTING:
                    disk
=
new PS3ShootingGameDisk();
                    
break;
               
case GameDiskType.FIGHTING:
                    disk
=
new PS3FightingGameDisk();
                    
break;
               
default:
                    disk
=
null;
                    
break;
            }

            
//insert disk to console

            
//load game

            
//play and enjoy it
        }
    }


代码1
从上述代码看,如果我们需要增加新的游戏盘,例如角色扮演游戏(RolePlayGameDisk),那么生成游戏盘部分(见注释create a game disk处)需要增加case分支,这里的对具体游戏盘对象实例化存在着变化的需求。根据设计原则 "封装变化(Encapsulate what varies)" 对这一对象实例化的需求进行封装。 引入一个新的类来封装和处理对象生成的需求,这个类叫做PS3DiskFacotry。

public
class PS3Player
    {
        
public
void PlayAGame(GameDiskType type)
        {
            
//Get a console

            
//Get a joystick

            
//create a game disk
            AbstractGameDisk disk = PS3DiskFactory.CreateGameDisk(type);
            
            
//insert disk to console

            
//load game

            
//play and enjoy it
        }
    }

   
public
sealed
class PS3DiskFactory
    {
        
public
static AbstractGameDisk CreateGameDisk(GameDiskType type)
        {
            
switch (type)
            {
               
case GameDiskType.RACING:
                    
return
new PS3RacingGameDisk();
               
case GameDiskType.SHOOTING:
                    
return
new PS3ShootingGameDisk();
               
case GameDiskType.FIGHTING:
                    
return
new PS3FightingGameDisk();
               
default:
                    
return
null;
            }
        }
    }


代码2
从上面的代码看PS3DiskFactory专门负责游戏盘(AbstractGameDisk的具体子类)的实例化过程,当有新的游戏盘增加时,也就是实例化过程的需求发生变化时,全部变化会单独发生在PS3DiskFactory里面,也就是可变化的需求被封装到一个类里面了,这就是Simple Factory的实现。

                               
登录/注册后可看大图

图2
由于对实例化需求的变化的封装,从图可见PS3DiskManager和具体的游戏盘类(PS3RacingGameDisk,PS3ShootingGameDisk和PS3FightingGameDisk等)彻底的解耦,PS3DiskManager只是依赖于他们共同的抽象类AbstractGameDisk和工厂类PS3DiskFactory。Simple Factory的作用就是用来封装“对象实例化可变化的需求”。
Factory Method
随着这个电玩店的发展,店里开始支持Wii游戏机销售和试玩,原先的系统需要更新符合这一新需求。参考Simple Factory的实现,我们可以很快速的实现Wii的需求。

public
class WiiPlayer
    {
        
public
void PlayAGame(GameDiskType type)
        {
            
//Get a console

            
//Get a joystick

            
//create a game disk
            AbstractGameDisk disk = WiiDiskFactory.CreateGameDisk(type);
            
            
//insert disk to console

            
//load game

            
//play and enjoy it
        }
    }

   
public
sealed
class WiiDiskFactory
    {
        
public
static AbstractGameDisk CreateGameDisk(GameDiskType type)
        {
            
switch (type)
            {
               
case GameDiskType.RACING:
                    
return
new WiiRacingGameDisk();
               
case GameDiskType.SHOOTING:
                    
return
new WiiShootingGameDisk();
               
case GameDiskType.FIGHTING:
                    
return
new WiiFightingGameDisk();
               
default:
                    
return
null;
            }
        }
    }

public
class WiiRacingGameDisk : AbstractGameDisk
    {
        
public
override
void Load()
        {
            Console.WriteLine(
"Load Wii racing game.");
        }
    }

   
public
class WiiShootingGameDisk : AbstractGameDisk
    {
        
public
override
void Load()
        {
            Console.WriteLine(
"Load Wii shooting game.");
        }
    }

   
public
class WiiFightingGameDisk : AbstractGameDisk
    {
        
public
override
void Load()
        {
            Console.WriteLine(
"Load Wii fighting game.");
        }
    }


代码3

                               
登录/注册后可看大图

图3
第一眼看是不是很容易实现了新的需求?Copy & Paste,稍稍修改一下就完了。可是有没有发现两个Player类除了对GameDisk的实例化以外,其他的一模一样。我们引进第二个设计原则 "面向抽象编程,而不是面向具体编程(Depend on Abstractions, not on Concretions)" ,增加对各个具体Player的抽象类AbstractPlayer.Client面向的是Player的抽象类而不是具体的Player。

public
abstract
class AbstractPlayer
    {
        
public
void PlayAGame(GameDiskType type)
        {
            
//Get a console

            
//Get a joystick

            
//create a game disk
            AbstractGameDisk disk = CreateGameDisk(type);

            
//insert disk to console

            
//load game

            
//play and enjoy it
        }

        
protected
abstract AbstractGameDisk CreateGameDisk(GameDiskType type);
    }

   
public
class PS3Player : AbstractPlayer
    {
        
protected
override AbstractGameDisk CreateGameDisk(GameDiskType type)
        {
            
return PS3DiskFactory.CreateGameDisk(type);
        }
    }

   
public
class WiiPlayer : AbstractPlayer
    {
        
protected
override AbstractGameDisk CreateGameDisk(GameDiskType type)
        {
            
return WiiDiskFactory.CreateGameDisk(type);
        }
    }


代码4

                               
登录/注册后可看大图

图4
引入对具体各个Players的抽象类AbstractPlayer后,在程序中可以声明AbstractPlayer的引用,而具体的Player对象的实例化过程留给具体的Player类(PS3Player或者WiiPlayer)来实现。AbstractPlayer声明CreateGameDisk方法负责实例化AbstractGameDisk的子类对象,具体的Player类负责实例化具体的AbstractGameDisk的子类。这个Method(CreateGameDisk)的行为就是一个Factory,所以称为Factory Method。下面是一个典型的Factory Method的UML图。

                               
登录/注册后可看大图

图5
从图5和图4可以看,AbstractPlayer就是一个Creator,而PS3Player和WiiPlayer是一个ConcreteCreator,CreateGameDisk()就是FactoryMethod(),AbstractPlayer只是定义实例化方法,但是不知道具体如何实例化对象,实例化那个具体的对象,这些都是由PS3Player和WiiPlayer负责的。在上述的实现,PS3Player和WiiPlayer是借助于Simple Factory来实例化对象。
Factory Method是一个推迟实例化的过程,在抽象类(Creator)定义实例化的行为(FactoryMethod),然后由具体的子类(ConcreteCreator)决定具体实例化的对象。其实在OO中,抽象类负责定义行为(在C#中为Method或者Property),子类负责实现行为,运行时动态调用不同行为称为 多态(polymorphism)。但是构造函数不能实现多态,Factory Method就是解决对象实例化的多态问题。Factory Method的别名也叫Virtual Constructor,为什么这样叫了,因为在C++里面实现多态都要定义Virtual Function(虚函数),但是Constructor是不能定义为virtual的,Factory Method恰恰解决这个问题,所以也就Virtual Constructor了。
Abstract Factory
上面讲述的Simple Factory和Factory Method解决了游戏盘(GameDisk)的对象的实例化过程,如果在Player中其他使用到的引用(例如游戏主机GameConsole和手柄Joystick)都需要实现实例化不同的对象。那么就不仅仅需要CreateGameDisk(),而且需要CreateGameConsole()和CreateJoystick()来实例化具体的对象。抽象类AbstractPlayer组合这些Factory Methods,实现如下。

public
abstract
class AbstractPlayer
    {
        
public
abstract AbstractGameDisk CreateGameDisk(GameDiskType type);
        
public
abstract AbstractGameConsole CreateGameConsole();
        
public
abstract AbstractJoystick CreateJoystick();
    }

   
public
class PS3Player : AbstractPlayer
    {
        
public
override AbstractGameDisk CreateGameDisk(GameDiskType type)
        {
            
return PS3DiskFactory.CreateGameDisk(type);
        }

        
public
override AbstractGameConsole CreateGameConsole()
        {
            
return
new PS3Console();
        }
        
public
override AbstractJoystick CreateJoystick()
        {
            
return
new PS3Joystick();
        }
    }

   
public
class WiiPlayer : AbstractPlayer
    {
        
public
override AbstractGameDisk CreateGameDisk(GameDiskType type)
        {
            
return WiiDiskFactory.CreateGameDisk(type);
        }

        
public
override AbstractGameConsole CreateGameConsole()
        {
            
return
new WiiConsole();
        }
        
public
override AbstractJoystick CreateJoystick()
        {
            
return
new WiiJoystick();
        }
    }

   
public
abstract
class AbstractGameDisk
    {
        
public
string Name { get; set; }
        
public
abstract
void Load();
    }

   
public
abstract
class AbstractGameConsole
    {
        
public
void InsertGameDisk(AbstractGameDisk disk) { }
        
public
void PluginJoystick(AbstractJoystick joystick) { }
    }

   
public
abstract
class AbstractJoystick
    {
    }

   
public
class PS3Console : AbstractGameConsole
    {
    }

   
public
class PS3Joystick : AbstractJoystick
    {
    }

   
public
class WiiConsole : AbstractGameConsole
    {
    }

   
public
class WiiJoystick : AbstractJoystick
    {
    }


代码5
AbstractPlayer不再负责PlayAGame的功能,只是声明了一系列产品的实例化的Methods。AbstractPlayer还是Factory Method。 我们需要实现PlayAGame的功能,基于设计原则"组合优于继承(Favor Object composition over inheritance)",我们定义一个新的类(Player),然后把工厂类(AbstractPlayer)作为一个引用组合到这一个类里面,这就是Abstract Factory模式的实现。


public
enum GameDevice
    {
        PS3,
        WII
    }

   
public
class Player
    {
        
private AbstractPlayer playerFactory;

        
public Player(GameDevice device)
        {
            
switch (device)
            {
               
case GameDevice.PS3:
                    playerFactory
=
new PS3Player();
                    
break;
               
case GameDevice.WII:
                    playerFactory
=
new WiiPlayer();
                    
break;
            }
        }

        
public
void PlayAGame(GameDiskType type)
        {
            
//Get a console
            AbstractGameConsole console = CreateGameConsole();

            
//Get a joystick
            AbstractJoystick joystick = CreateJoystick();
            console.PluginJoystick(joystick);

            
//create a game disk
            AbstractGameDisk disk = CreateGameDisk(type);

            
//insert disk to console
            console.InsertGameDisk(disk);

            
//load game
            
//play and enjoy it
        }

        
private AbstractGameDisk CreateGameDisk(GameDiskType type)
        {
            
return playerFactory.CreateGameDisk(type);
        }
        
        
private AbstractGameConsole CreateGameConsole()
        {
            
return playerFactory.CreateGameConsole();
        }

        
private AbstractJoystick CreateJoystick()
        {
            
return playerFactory.CreateJoystick();
        }
    }


代码6

                               
登录/注册后可看大图

图6
以下为一个经典Abtract Factory的实现,用于对比参考

                               
登录/注册后可看大图

图7
Player(Client)完全不知道GameConsole,Joystick和GameDisk到底如何实例化的,这些都又具体工厂(PS3Player或者WiiPlayer)来负责一系列相关联的产品(也就是对象)的实例化,这一系列相关联的产品称为产品族(product family)。Player根据GameDevice借助AbstractPlayer实例化产品族,产品族下的所有产品具体协同工作,而Player只是依赖于产品组的产品的抽象类而不是具体类,也就是说,产品族的替换不会影响Player类的PlayAGame()。这一特性适合可替换产品族的设计,例如不同桌面主题的设计和不同数据库访问组件的设计,ADO.net的数据库访问层就是基于Abstract Factory模式设计的。
Player的构造函数中选择具体工厂也是用了条件选择(switch),这里可以通过Simple Factory来对具体工厂的实例化。
Abstract Factory有几个特点:
1.每次产生一系列相关联的产品,例如WiiGameConsole,WiiJoystick和WiiRacingGameDisk等等,他们之间是协调工作,例如CreateGame有InsertGameDisk方法,表示WiiGameConsole和WiiRacingGameDisk协调工作。
2.不同产品族直接的产品不可以相互替换,例如PS3Joystick不能用于WiiGameConsole。
3.使用Abstract Factory一般在产品族相当固定的情景下,例如XBox游戏机也是有GameConsole,Joystick和GameDisk,那么实现XBoxPlayer等产品族就可以支持XBox游戏机,但是如果需要更改产品族的产品,例如某新型游戏机不使用GameDisk而是使用HardDisk来Load游戏的话,那么现有的设计就不能支持这一新型游戏机。
这些我对Simple Factory,Factory Method和Abstract Factory的想法,欢迎指教。
   
Jake's Blog in 博客园 -- 精简开发 无线生活
作者: procoder    时间: 26-4-2009 12:33
没人愿意k一下?
作者: ubuntuhk    时间: 26-4-2009 13:04
标题: 回复 #2 procoder 的帖子
对设计模式很有兴趣,不过从来没接触过,谢谢你的介绍!
作者: stgeorge    时间: 26-4-2009 13:56
怎么K? 讨论设计模式?  空心的菱形表示聚合,实心的菱形表示组合。可以参考一下《UML参考手册》中的说明。空心的菱形表示Wheel对象并不随Car的创建而创建,销毁而销毁. 实心菱形表示Department对象随Company对象的创建而创建,销毁而销毁。没有明白你为什么用这个放在你的uml图中。因为player并不和abstract disk有直接的聚合(或组合)关系,只是调用具体的子类对象。

看了你的文章,有点晕。好像和以前自己的理解有区别:
先简单讨论一下。
1. 简单工厂: 单独的creator类,送进去参数,返回abstruct product.(但实际上是concrete product). 我猜你第一个是不是想表达这个意思?
作者: stgeorge    时间: 26-4-2009 14:01
补充一下,一般我们常用 static 的 createProduct (ptype type)在简单工厂中。
优点:client doesn't care 如何创建 concrete product, 解耦了。逻辑放在 creator 的 static createProdcut中。
缺点: creator 的 static createProdcut成了万能型选手。每次都要改这里。当产品有hierarchy时,死翘了。
作者: stgeorge    时间: 26-4-2009 14:12
有点忘了,子类是否能继承基类的静态方法?你要是能确定就回复一下。
我印象是能继承,但无法override. 所以才导致factory method的出现。
刚才说完,static factory method (simple facotry)
接下来再谈: factory method
factory method中,creator 不再负责 create了,它当爸爸了。create concrete product 的脏活累活,由他的儿子(马仔)来干。
作者: stgeorge    时间: 26-4-2009 14:32
因为不是static method所以可以继承并修改。concrete creator就可以自由繁殖了。好处是:
原来在client端:想要“你是电,你是光”
基类变量 = new Creator电();
物质(实际上指向电)=基类变量. create( ); //与static factory method 区别,不用传值。因为已经知道是Creator电()。

基类变量 = new Creator光();
物质(实际上指向光)=基类变量. create( ); //与static factory method 区别,不用传值。因为已经知道是Creator电()。

现在在client端:想要“你是电,你是光,你是唯一的神话”
三步搞定:
1. 唯一的神话:物质
2. Creator唯一的神话: 基类
3. client端加上:
基类变量 = new Creator唯一的神话();
物质(实际上指向唯一的神话)=基类变量. create( ); //与static factory method 区别,不用传值。因为已经知道是Creator唯一的神话()。

完全follow open-close principle. 这下世界清净了。
作者: stgeorge    时间: 26-4-2009 14:48
刚刚流得滑了几天,唉,人生难免踩上狗屎。用户要求听小沈阳版的,宋丹丹版。。。
用户:这个可以有,
DEV: 这个真没有
用户:没有?把你老板找来!
DEV: 没有
用户:没有?Only you...

我们piapai的昏了。大象,大象,你的需求为什么这么长?
无奈呀,我们请出了abstract facotry.
很简单:就是在creator中,加上不同的种类,以便让产生不同的盗版。

我的故事讲完了。
欢迎讨论
作者: procoder    时间: 26-4-2009 17:26
根据各方讨论,我把文章更新了,可见这里 我的实用设计模式之四-Simple Factory,Factory Method和Abstract Factory

[ 本帖最后由 procoder 于 26-4-2009 17:46 编辑 ]
作者: procoder    时间: 26-4-2009 17:38
原帖由 stgeorge 于 26-4-2009 13:56 发表
因为player并不和abstract disk有直接的聚合(或组合)关系,只是调用具体的子类对象。

我使用Aggregation因为考虑到player会把abstract disk作为一个成员变量保存起来,到底是Aggregation,Composition或者Dependency要看具体的应用。
两个类之间的关系,例如类A和B。
如果是B是A成员变量,而且BA的构造函数中生成(new),那么就是Composition。
如果是B是A成员变量,而且B不在A的构造函数中生成(new),而是在有需要的时候才new,那么就是Aggregation。
如果A在某个函数中使用了B作为局部变量,那么就是Dependency。

在经典的实现中一般画成DirectedAssociation,Composition和Aggregation都是Association的一种,看具体应用。
但是我想这里的Aggregation不影响模式的表述,因为这个只是Client和Factory的关系,模式准确与否,看Factory自己的Hierarcy。
作者: procoder    时间: 26-4-2009 17:40
原帖由 stgeorge 于 26-4-2009 13:56 发表
1. 简单工厂: 单独的creator类,送进去参数,返回abstruct product.(但实际上是concrete product). 我猜你第一个是不是想表达这个意思?

就是这个意思。
作者: black_zerg    时间: 26-4-2009 18:07
提示: 作者被禁止或删除, 无法发言 个人拙见,这些个模式还是太复杂。一个最常用的工厂模式就画出这么一个大图,那些二十几个模式我从来就没有同时想清楚过。
其实工厂模式就一个原理,为了把对象的接口和实现分开,搞一个控制器用来取对象。一句话不就完了,所以我们有个控制器类来控制这个事情。结果这些人把这个控制器类再给抽象化成接口和实现,居然又造出个模式,要按这个逻辑,我能造出无数个设计模式来,只能让人糊涂么。这些个模式太冗长了。当然我也不能肯定这么二十几个都没用,但始终觉得5,6个也就够了。
作者: coredump    时间: 26-4-2009 21:29
标题: 回复 #12 black_zerg 的帖子
问题的复杂度不同,采用的设计方案也不同。杀鸡用牛刀不好,杀牛用铅笔刀也很愚蠢。
作者: procoder    时间: 27-4-2009 10:46
标题: 回复 #12 black_zerg 的帖子
模式确实是在那几个设计原则的基础上推导出来,GoF的模式只是对某些场景下的设计手法,不是所有常见都用得上,简单场景用简单的,我用得最多是Simple Factory,那个还不算GoF模式呢。Abstract Factory只是用在写数据库的wrapped class。ADO.net就是使用他的,如果在这一的场景下,用Abstract Factory非常方便,不用反而不好。
作者: procoder    时间: 27-4-2009 11:02
原帖由 stgeorge 于 26-4-2009 14:12 发表
有点忘了,子类是否能继承基类的静态方法?你要是能确定就回复一下。
我印象是能继承,但无法override. 所以才导致factory method的出现。
刚才说完,static factory method (simple facotry)
接下来再谈: facto ...

子类可以继承基类的静态方法,但是不能多态,static是编译时的,polomorphism是运行时的,但是这个不是“导致factory method的出现”的原因。“导致factory method的出现”的原因可以从设计原则“面向抽象编程而不是面向具体编程”推出。
作者: godspeed    时间: 27-4-2009 18:51
faint. GOF. 读个关于GOF的开头就坚持不下去了。痛苦。
作者: hoopoos    时间: 28-4-2009 11:52
你的例子很好,不过最后的关于Abstract Factory的图有点乱,我画了一个,仅供参考
作者: glite    时间: 28-4-2009 12:59
对模式很感兴趣,不过如果为了模式而模式就不是特别有必要了。

.net中,一般的面向接口编程的模版也已经非常不错了。
作者: procoder    时间: 29-4-2009 08:49
thank you very much.
I have a updated UML for the Abstract Factory here.


                               
登录/注册后可看大图

作者: procoder    时间: 29-4-2009 08:52
标题: 回复 #18 glite 的帖子
I agree "为了模式而模式就不是有必要了", anybody knows it.
This writing I wrote just demonstrate when need to use, how to use. I am NOT talking about U SHOULD use it any time.
作者: procoder    时间: 29-4-2009 08:55
From Simple Factory to Abstract Factory, the requirements are changing. Which patterns should be use just depends on which scenario it is. No one said using patterns is always better than non-using cases.
作者: procoder    时间: 29-4-2009 09:00
标题: 回复 #17 hoopoos 的帖子
In this case, I can not use GameDevice as the abstract class, because AbstractGameConsole, AbstractJoystick and AbstractGameDisk have different methods. Player have to know about each single one(AbstractGameConsole, AbstractJoystick and AbstractGameDisk) and use their specific methods.

Anyway, Thank you very much. Your design is absolutely clearer than mine. Good on you.
作者: hoopoos    时间: 29-4-2009 09:55
我同意你的说法,不过,鉴于一个game device通常是3部分搭配组成,那么也可以换一个形式,只用一个factory method来创建,我的设计和你的区别在于,我的client是个老年人或是小孩子,不懂一个游戏机由主机,手柄,光盘,组成,他只知道说:给我一台Wii游戏机,现在最火的!。你的Client呢,是个懂行的玩家,他熟门熟路的说,老板,来个欧版的主机,日版的手柄,08版的Wii Sport!

Anyway,你已经表示了Simple Factory,Factory Method和Abstract Factory,谢谢你的分享,你可以继续你的模式之旅!
作者: procoder    时间: 29-4-2009 12:53
标题: 回复 #23 hoopoos 的帖子
Thanks for your ideas. I will keep going, I hope we can communicate more about Design Patterns.
作者: glite    时间: 30-4-2009 03:13
楼主,请问一下,澳洲那边,.net一般是如何去做架构以及设计的?

在我们目前正在准备开发的项目中,项目经理让我们采用微软的petshop4那样类似的设计,在这样的设计中,我个人认为多多少少也用到了一点简单工厂方面的东东。

恩,有点感触。。。其实真的感觉自己有点没出息,只知道跟着微软走啊走。
作者: procoder    时间: 30-4-2009 08:41
标题: 回复 #25 glite 的帖子
我现在主要做compact framework,我开发的产品设计部分与平台无关,就是说设计不考虑语言特性,实现的时候才考虑语言特性。当然做j2ee那些可能不一样,平台有自己的模式,基于一般GoF的扩展,如果你用petshop的话,多多少少会用到些模式,我觉得随着问题复杂度越大,用到模式的机会越多,例如你有数据库访问层,就会用到Abstract Factory。

Simple Factory用的很多,也是最常用的一个,我自己平常用的经验。可是Simple Facotory不是GoF的模式。
作者: procoder    时间: 30-4-2009 08:48
跟着微软走没什么不好的,我就比较喜欢做.net。一起进步吧。
作者: black_zerg    时间: 30-4-2009 11:29
提示: 作者被禁止或删除, 无法发言 我的意思是认为这些模式不精炼,就以工厂模式为例,几个工厂模式其实都一回事。
一个应用分解为三个,接口,实现,以及一个取对象的工厂。所以我倾向于把他们看做一个模式,比较好理解和掌握。 那些后来衍生出的东西根本就不能算新模式,抽象工厂这个模式无非是 取对象那个类再次用工厂模式分解一下,另外一个则是一个简单的参数化实现。当然这三个工厂模式本身算是很简单的,并不难理解,可是看到后面那么多,什么桥接什么的就看得很头大。模式本身应该是很精炼的,而并不是一个实现,应该更倾向于设计思路和原则。
作者: sliuhao    时间: 30-4-2009 13:10
没有仔细看代码,看看UML图:
探讨:
1. 是不是应该在Abstract layer上增加interface, 如你所说,接口优于实现
再者你用Abstract layer应该以后会用templete method, 大多如此。而且一但用templete method, 势必不会public, 而会在Abstract layer里面做,这样你的Abstract layer就不全是接口了

2.GameCosole,GameDisk,GameStick应该可以做一个更大的抽象工厂,而非都用类似builder的方式

3.真的是个电玩系统, 可能用不到那么多的层次,看平台的限制.....我更倾向用Jython.....
作者: procoder    时间: 30-4-2009 22:31
标题: 回复 #28 black_zerg 的帖子
根据GoF的定义,三个还是有不同的应用场景的,不是同一个。
作者: procoder    时间: 30-4-2009 22:35
标题: 回复 #29 sliuhao 的帖子
你看我更新的贴,Abstract Class可以用interface来代替,其实Abstract Class里面我用了template模式,这篇文章的例子是我为了表达3个模式的演变过程想出来的,真实系统会有很多模式混用的时候。

写这篇文章不是讨论要不要用模式,是假设要用,如何用,在什么场景下用。

就行HF里面strategy模式,很多人都说bridge,因为模式就是在设计原则下推导的结果的经验总结,懂得原则可以推断出好多类似GoF的模式。我写的目的是尽量反映GoF。不是根据原则去推导出各种各样不同的模式,算是学习的总结。
作者: key    时间: 1-5-2009 01:01
标题: 关于实现细节的一点不同看法
原帖由 procoder 于 24-4-2009 21:48 发表
这是我自己的一些想法,论坛里有好多高手,请指教。
Simple Factory
先从Simple Factory开始讲起,假设模拟一个电玩店的试玩系统,这个电玩店专卖出售PS3的游戏机和提供试玩服务,当一个用户想试玩的时候,需要选择 ...


pro的文章很不错,我看到得益很大(这不是客气话,是实话,真的很感谢)。。。。

不过我对照了GoF的Diagram后,有一点不同的看法。我觉得,如果参照原始的GoF,
采用Abstract Factory,我会得到下面第二个图的Diagram。
而我也觉得,以你所举的例子来看,这个Abstract Factory的“实现”可能更合适。

1. 虽然没有这样的假设,但从你的整个解说过程中,我发现,每种Game都有Racing,
Shooting, Fighting这三种分类。所以,你的Factory类/接口中,定义了三种Game
的产生实现。个人认为,采用Switch和采用explicit method的做法有其相似之处。
当然,Switch有自己的优势;我画成method只是为了更接近GoF的原图。

2. 第一层的Game抽象和Racing...等三种Game是继承关系,这个关系我觉得不需要表示
在所有的类图中,因为过多的线条会影响阅读和理解;在其他图中去掉这种继承关系,我们
可以更清楚地看到Abstract Factory在这里更合适,也更接近GoF的原图。

3. Player应该是一个client。这一点在你文章的最后有所反映。但你选择AbstractPlayer
来做为Creator的名称,可能不是太合适。

原文中,你提到Joystick,GameConsole等元素,我在这里没有涉及。
作者: key    时间: 1-5-2009 01:14
标题: 认同关于演变的过程
原帖由 procoder 于 30-4-2009 22:35 发表
你看我更新的贴,Abstract Class可以用interface来代替,其实Abstract Class里面我用了template模式,这篇文章的例子是我为了表达3个模式的演变过程想出来的,真实系统会有很多模式混用的时候。

写这篇文章不是讨 ...


pro同学关于设计的演变过程,我很欣赏。其实我自己也一直在想这个问题:
拿到一个需求,因为某些原因,如经验,灵感,等,会有一些东西不期然的跑
到我们的脑子里。而这个东西很可能是简单的、不完美的。

更重要的是,需求是不断的清晰化的,是过程化的。这个时候我们必须不断演化
我们的系统,加入新的变化,加入新的元素。

谢谢你的分享。

不过,可能是我理解上的问题,我觉得从Simple Factory到Abstract Factory,
这个过程应该是不同的选择过程,而不是一个“升级”“进化”的过程。在不同的场合下,
选择更合适的“模式”,使我们的实现更容易、更直观。
作者: sliuhao    时间: 1-5-2009 09:27
标题: 回复 #31 procoder 的帖子
你要说是 学习的总结 , 那就没什么好讨论的啦,呵呵.....

我的经验是, 要学pattern,就该忘记pattern。建议你看看GoF之前的开山之著, 亚历山大的 "永恒建筑之道" - the endless building.一本在恒建筑业没什么名气的书,确是模式的开山之书......
作者: procoder    时间: 1-5-2009 09:29
原帖由 key 于 1-5-2009 01:01 发表


pro的文章很不错,我看到得益很大(这不是客气话,是实话,真的很感谢)。。。。

不过我对照了GoF的Diagram后,有一点不同的看法。我觉得,如果参照原始的GoF,
采用Abstract Factory,我会得到下面第二个图 ...

非常感谢你认真的看,谢谢你。
我写这篇文章的时候,一直运用了原有的代码,例如Factory Method用来了Simple Factory的工厂类,这样导致了设计和GoF的有不同,我认为我实现的不同的在GoF的基础上扩展了,例如把某个直接生成的过程使用了工厂类。当然这样做的好处是有连续性,坏处明显就是增加了阅读的难度,因为实现比GoF的经典实现多了一两步。还有你的建议很好,在实现不同模式的时候把class名字改一下,而不是只是沿用原先的class的名字。

关于你“第一层的Game抽象和Racing...等三种Game是继承关系”的实在,我觉得是refactoring里面replace conditional to polymorphism 的做法,am i right?

其实如果只是描述Abstract Factory的话,应该把各个游戏碟类型去掉,concrete class只有PS3GameDisk和WiiGameDisk。Joystick,GameConsole我还是会保留,因为 Abstract Factory 用在生产产品族的需求,各个产品族之间的产品不能替换,一个产品族内部的产品相互协作。
作者: hoopoos    时间: 1-5-2009 09:33
原帖由 procoder 于 1-5-2009 09:29 发表


其实如果只是描述Abstract Factory的话,应该把各个游戏碟类型去掉


你果然看出来了,在描述一个模式的时候,最好把不直接相关的关系省略掉,突出重点,另外就是给类和接口起名最好贴切一点。
作者: procoder    时间: 1-5-2009 09:44
原帖由 key 于 1-5-2009 01:14 发表


pro同学关于设计的演变过程,我很欣赏。其实我自己也一直在想这个问题:
拿到一个需求,因为某些原因,如经验,灵感,等,会有一些东西不期然的跑
到我们的脑子里。而这个东西很可能是简单的、不完美的。

更 ...

关于演变的过程,每个引入新模式的时候的大前提是需求变化,做Simple Factory的时候是完全不知道会有Wii的需求,需求是在实现Simple Factory添加的,我们做设计设计的时候会把变化的尽量多想,在这个case,可能一开始想到Abstract Factory或者其他,有对“实例化过程的封装”的需求,产生Simple Factory。由于加入Wii的对象“实例化过程的封装”的需求,有出现”面向抽象优于面向具体“,出现了Factory Method。有对产品族(加入GameConsole,joystick)的“实例化过程的封装”的需求,出现了Abstract Factory。

Simple Factory不是必然演变成Abstract Factory,如果这样真是为了模式而模式。而是在需求的驱动下,场景符合的情况下进行演变,这文章想表达就是这个意思。
作者: procoder    时间: 1-5-2009 09:48
标题: 回复 #34 sliuhao 的帖子
我写这个表达在什么场景下用,如何用。我的用法尽量忠于GoF的经典实现。

实际工作心中应该是设计原则,而不是模式,推倒过程中,觉得这个场景符合某个模式,然后就模式一下,然后再扩展一下。
作者: procoder    时间: 1-5-2009 09:51
标题: 回复 #36 hoopoos 的帖子
说实在,我开始就发现这个问题,这是我延续性(为了原先Simple Factory等已有的实现)和简化性的权衡。

其实演变过程中,我忘记了一个重要的refactoring实在,rename。如果我在表达一次,可能会更清楚和更好了,这也我的写东西的目的:多想,多写,多总结,多交流。
作者: 电视机    时间: 30-11-2011 20:53
不知楼主的程序库规模有多大?20万行的代码有没有?

以我15万行有效代码的经历,觉得设计模式根本用不着。




欢迎光临 FreeOZ论坛 (https://hioz.im/ibbs/) Powered by Discuz! X3.2