浅谈《设计模式》

设计模式,是一个程序员稍微进阶点的问题了。

之前在开发的时候用过不少方法,包括Instance单例模式(最常见的就是游戏中的各种全局管理器等),以及订阅-发布(又称观察者模式)等,

当时用的时候纯粹是觉得用这些方法能够优雅些,能够使代码整洁、规整下。也没想过这些事什么,

直到最近无意看到一本书《大话设计模式》,才意识到编程中的设计问题。

原来编程不只是能写出代码,能写出功能,如何把代码写得更加优雅,更加健硕,能减低各种耦合,能考虑后期维护成本,才是王道。

我认真地读了下这本书,写一些笔记,算是心得体会吧。

(注:学习设计模式首推《深入浅出设计模式》,一本外国人写的很有趣的书,《大话设计模式》是国人模仿那本书写的,总体还算不错,

但我发现有一些问题,书里有些例子举得有些牵强,感觉是为了举例而举例,比如第一章,在设计计算器那块,明明可以用委托实现的简单功能,

作者偏偏要举工厂模式的例子,不知道是我领悟不够还是怎么的,反正我觉得举错例子还不如不举例子)

 

先来个UML类图说明吧,介绍模式的时候偶尔会用到:

 

一、简单工厂模式

简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。(我懒,直接复制百度百科)

就好比,你在游戏里需要为主角创建衣服,裤子,鞋子,帽子等物品,但你总不能为每一种物品的创建都单独写一种实现吧,

player.shoe = new Shoe();
player.trousers = new Trousers();
player.clothes = new Clother();

这种写法明显有问题,具体是什么问题,我TM全忘了了。。==,只是觉得这样写很不好看。

正确的做法是:

1.将所有要创建的对象抽象出一个共同的父类,例如这里:

class Equip{},里面描述所有子类的相同信息

2.所有要生成的物品继承Equip,在工厂类中为每种物品实现生产的具体函数,例如:CreateShoe()

工厂类的大概实现如下:

复制代码
class Factor{

public static Equip Create(EquipType type){
  switch(type){
    case Shop:
      return CreateShoe();
      break;
    case Clother:
    ...
    }
  }

Equip createShoe(){}
...
}
复制代码

3.创建物品时,直接通过工厂向工厂类中传入参数来指定生成哪一种产品:

player.shoe = (Shoe)Factory.Create(EquipType.shop);
player.clothes = (Clothes )Factory.Create(EquipType.clothes );

最后,我们分析下该模式的优缺点:

复制代码
优点
工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点
由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。
当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
这些缺点在工厂方法模式中得到了一定的克服。
复制代码

 

二、单例模式

单例模式是我最早接触的模式了,也是比较简单而且很实用的模式。
单例模式是一种常用的软件设计模式。在它的核心结构中只包含 一个被称为单例类的特殊类。通过单例模式可以保证系统中 一个类只有一个实例而且该实例 易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
总的来说,单例模式有三个要点:
1.一个类只有一个实例
2.对外界提供访问接口
3.它必须自动创建这个类的实例
由此三点,我们可以简单地描述下单例的设计方法:
1.将单例类的构造函数隐藏,防止被实例化
2.提供一个public的静态函数,用于访问该单例
3.在访问该单例的函数中判断该单例是否已经实例化,如果没,则实例化自身一次
简单的代码实现如下:
复制代码
// 懒汉式
public class SingletonClass{
    private static SingletonClass instance=null;
    public static SingletonClass getInstance()
    {
        if(instance==null)
        {
               instance=new SingletonClass();
        }
        return instance;
    }
    private SingletonClass(){
    }
}
复制代码

但以上有些问题,就是在多线程下易出错误,比如两个线程同时调用该单例,而且该单例还未实例化,

那么就会出现以下问题,线程1访问时,由于instance == null,所有线程一会实例化该单例,而线程2访问时,

instance也有可能为null,所以他也实例化一次,这就会出现问题,解封方法就是用双向锁:

复制代码
// 双从锁定
class Singleton{
    private static Singleton instance=null;
    private static readonly object syncRoot = new object();
    private Singleton(){
        //do something
    }
    public static Singleton getInstance(){
        if(instance==null){
            lock(syncRoot){
                if(null==instance){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
}
复制代码

如果是C#语言的话,还可以用静态初始化的方法,那样更简洁:

复制代码
// C#静态初始化方法(即饿汉式方法)
public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton GetInstanceJ()
    {
        return instance;
    }
}
复制代码

扩展: Unity3D实现继承于MonoBehaviour的单例

由于MonoBehaviour的特殊性(不能new,自动消耗,自动调用周期函数),所以实现起来稍微有点不一样:

要点:

a.其他部分跟普通单例差不多,新增的Awake里的代码,是由于U3D有AddCompent的特性,避免单例组件被多次Add导致重复生成单例对象。

b.DontDestoryOnLoad是由于U3D的所有对象都在切换场景时释放,为了避免单例对象被释放,应该加这一句。

还可以将上面的实现抽成抽象,做成一套模板,这样就可以省事很多,毕竟一个游戏里可能有会很多单例管理器:

 

至于泛型的理解,参考我这篇文章中的第八条。

注:该模版只是将单例的通用创建以及处理方式抽象出来而已,被写死在模板里,如果要利用此模板实现特定的单例,

必须得用继承,例如:class MyUIManager : Singleton<UIManager>,然后使用的时候就MyUIManager.Instacne即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值