游戏中常用的设计模式总结

Design Pattern Summary

一、概述

1.什么是设计模式?
每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动(是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结)。
2.什么时候使用设计模式?
面对很多数据类型
如何组织规划这些数据类型——设计模式:面对很多数据类型 组织规划

二、创建型——关注对象创建的问题

1.SINGLETON(单件,单例) 关键词:唯一

(1)问题:

如何保证一个类只创建一个对象,且该对象全局共享。

(2)意图:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

(3)适用性:

• 当类只能有一个实例而且客户可以从一个众所周知的访问点(公共静态)访问它时。
• 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

(4)结构:

私有的构造方法。
包含一个静态私有字段返回自己的类型。
包含一个静态公共方法返回自己的类型(用于返回自己类的唯一实例)。
在这里插入图片描述

(5)实例:

a.多种不同写法的举例(见Month01笔记p101)

//饿汉模式
class Singleton {
	private Singleton(){}
	static private Singleton m_instance = new Singleton( ); 
	public static Singleton instance( ) { return m_instance; } 
}
//懒汉模式
class Singleton {
	private Singleton(){}
	static private Singleton m_instance;		
	public static Singleton Instance()
	{
		if (m_instance == null)
			m_instance = new Singleton();
		return m_instance;
	}
}		

b.EasyTouch中提供的单例模式抽象父类:

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
	private static T m_Instance = null;
    //当在设计阶段脚本并不挂在物体上时,还想要单例模式就要自己按单例模式的套路写
    //运行时需要脚本的唯一实例时调用instance即可
	public static T instance{
        get{
			if( m_Instance == null )
			{
                //先在场景中找
            	m_Instance = GameObject.FindObjectOfType(typeof(T)) as T;
                if( m_Instance == null ){
                    //创建游戏物体并把T脚本挂在物体上并返回T脚本组件(唯一实例)
                    m_Instance = new GameObject("Singleton of " + typeof(T).ToString(), typeof(T)).GetComponent<T>();
				  	m_Instance.Init();
                }
            }
            return m_Instance;
        }
    }
	//这里用Awake的原因:
	//因为unity中脚本挂在物体上系统自动帮我们把脚本类实例化了,
	//而如果是单例模式就裂开了,相当于系统自动给我们new了,但我们并不用new也不能new
	//故我们需要在Awake中找到唯一实例记录在变量中,只要不是空在instance中不会再new了,直接返回
    private void Awake(){
        if( m_Instance == null )
        {
            m_Instance = this as T;
        }
    }
    //提供初始化
    public virtual void Init(){}
    //退出时清理工作
    private void OnApplicationQuit(){ m_Instance = null;}
}

c.ARPGDemo中写的单例游戏对象池(继承EasyTouch中提供的单例模式抽象父类):

public class GameObjectPool :MonoSingleton<GameObjectPool>{......}

只需要继承EasyTouch中提供的单例模式抽象父类即可实现单例游戏对象池,别的地方根本不用动。

2.SimpleFactory 简单工厂 关键字:多选一

(1)问题:

客户在使用对象时,面临在多个类型中选择一个来创建对象,具体对象的类型可能有变化。

(2)意图:

定义一个全局的工厂类,负责类型的选择及对象的创建初始化,从而实现将创建与表示分离。

(3)适用性:

• 当一个类(抽象类)不知道它所必须创建的对象的类(具体子类)的时候。
• 当类将创建对象的职责委托给一个全局创建点,客户不需要关心具体类型,需要对象 的时候,找全局创建点即可.

(4)结构:

在这里插入图片描述

(5)说半天鸟话,一句话总结:

工厂是可以根据需要动态创建不同的对象的接口类或方法,这些对象往往有共同的父接口或父类,使用工厂模式,可能多做一些工作,但会给系统带来更大的复用性和可维护性。工厂方法:一次只能创建一个对象。

(6)解决方案【步骤】

a.创建父类产品以及所有的子类产品
b.创建一个工厂类,在工厂类中定义一个方法即工厂方法
c.实现工厂方法,即根据不同的条件创建并返回不同的对象

(7) 实例:

我们在ARPGDemo中的施放器配置中需要配置技能的攻击选择模式

//根据攻击模式确认攻击选择实例化所用的类
//IAttackSelector是接口,CircleAttackSelector、CircleAttackSelector是子类
       public static IAttackSelector CreateAttackSelector(SkillData skillData)
		{
            IAttackSelector attackSelector = null;
            switch (skillData.damageMode)
			{
                case DamageMode.Circle:
                    attackSelector = new CircleAttackSelector();
                    break;
                case DamageMode.Sector:
                    attackSelector = new SectorAttackSelector();
                    break;
            }
            return attackSelector;
        }

3.Abstract Factory 抽象工厂 关键词:系列

(1) 问题:

多个类型中以系列化的方式创建对象

(2)意图:

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类

(3) 适用性:

• 一个系统要独立于它的产品的创建、组合和表示时。(创建与表示分离)
• 一个系统要由多个产品系列中的一个来配置时。(多系列中选一系列)
• 当你要强调一系列相关的产品对象的设计以便进行联合使用时(同系列中多种产品)
• 当你提供一个产品类库,而只想显示它们的接口而不是实现时。(客户端依赖抽象)

(4)结构:

在这里插入图片描述

(5)为什么有了工厂模式还要用抽象工厂?

工厂方法设计模式虽然使对象的创建与使用进行了分离,但一次只能创建一个对象。
它不能实现一次创建一系列相互依赖的对象,为此我们需要学习抽象工厂设计模式。

(6)实例:

这个例子很好,因为中国工厂只能做中国坦克和飞机,美国工厂只能做美国坦克和飞机。

public abstract class 抽象工厂{
public string 国家;
    abstract public 坦克 生产坦克();
    abstract public 战斗机 生产战斗机();
}
public class 美国工厂 : 抽象工厂{
	public override 坦克 生产坦克(){ return new 美国坦克();}
	public override 战斗机 生产战斗机(){ return new 美国战斗机();}
}
public class 中国工厂 : 抽象工厂{
	public override 坦克 生产坦克(){ return new 中国坦克();}
	public override 战斗机 生产战斗机(){ return new 中国战斗机();}
}
public abstract class 战斗机{}
public class 中国战斗机 : 战斗机{}
public class 美国战斗机 : 战斗机{}
public abstract class 坦克{}
public class 中国坦克 : 坦克{}
public class 美国坦克 : 坦克{}

//调用:
class Program
{
	static void Main(string[] args)
	{
		抽象工厂 factory = new 中国工厂();
		factory.国家 = "中国";
		坦克 tank = factory.生产坦克();
		战斗机 airplane = factory.生产战斗机();
		Console.WriteLine(factory.国家);
		Console.WriteLine(tank);//->***.中国坦克
		Console.WriteLine(airplane);//->***.中国战斗机
		Console.ReadLine();
	}
}

4.Builder 建造器,生成器 关键词:组装

(1)变化:

复杂对象:对象中的成员可能是其它类型的对象。
复杂对象的结构是稳定的,但是组成结构的具体对象是变化的。
我的理解:说白了就是复杂对象干的所有事都是一样,但是干的效果/方式不同。

(2)问题:

复杂对象对于子部件构建的过程。

(3)意图:

将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示结果。

(4)适用性:

• 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
• 当构造过程必须允许被构造的对象有不同的表示时。

(5)结构:

在这里插入图片描述

(6)实例:

出去吃饭时长要点套餐(主食+菜+饮料),这些由服务员去点,客户负责选择

abstract class 套餐{
	abstract public void 返回主食();
	abstract public void 返回饮料();
	abstract public void 返回菜();
}
class 服务员{
	public void Construct(套餐 abstractBuilder){
	abstractBuilder.返回主食();
	abstractBuilder.返回饮料();
	abstractBuilder.返回菜();
	}
}
public class 红烧肉套餐 : 套餐{
	override public void 返回主食()
	{
		Console.WriteLine("米");
	}
	override public void 返回饮料()
	{
		Console.WriteLine("可乐");
	}
	override public void 返回菜()
	{
		Console.WriteLine("红烧肉菜");
	}
}
public class 麻婆豆腐 : 套餐{
	public override void 返回主食(){...}
	public override void 返回饮料(){...}
	public override void 返回菜(){...}
}
public class Client{
	public static void Main(string[] args)
	{
		红烧肉套餐 concreteBuilder = new 红烧肉套餐();
		服务员 director = new 服务员();
		director.Construct(concreteBuilder);
		Console.ReadKey();
	}
}

三、结构型——关注类或对象的结构,组织类与类的关系

1.Facade (外观,门面) 关键词:向导

(1)问题:

客户代码访问业务代码时,需要使用多个业务对象才能实现,此时不应该让客户代码来组织访问流程,应将访问流程的组织交由外观类来实现,提供给客户一个简单的访问接口即可.

(2)意图:

为子系统中的一组接口提供一个一致的界面, F a c a d e模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。我的理解:说白了就是把很多子系统的功能放在一个系统中调用,用户只需调用一个系统,方便了用户使用,比如ARPGDemo中的角色外观类、技能外观类。

(3)适用性:

• 当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越
复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容
易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。
F a c a d e可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过 f a c a d e层。
• 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入 f a c a d e 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
• 当你需要构建一个层次结构的子系统时,使用 f a c a d e模式定义子系统中每层的入口点。
如果子系统之间是相互依赖的,你可以让它们仅通过 f a c a d e 进行通讯,从而简化了它们
之间的依赖关系。

(4)结构:

在这里插入图片描述

(5)实例:

在ARPGDemo中我们的玩家需要施放技能来攻击敌人,然而技能需要的准备工作很多,写了很多类来实现技能的加载、信息配置、施放,如果这些类都由玩家来使用,那么复用性太差了,调用也不方便,所以我们把施放技能所需要的方法都再封装入技能外观类CharacterSkillSystem的方法AttackUseSkill中:
我们通过按钮施放技能,就可以像下面这样很方便的调用,而不用管各种技能类的细节。

private void ButtonDown(string buttonName)
{
	CharacterSkillSystem css = GetComponent<CharacterSkillSystem>();
	switch (buttonName)
	{
		case "Skill1":
			css.AttackUseSkill(11, false);
			break;
		case "Skill2":
			css.AttackUseSkill(12, false);
			break;
		case "Skill3":
			css.AttackUseSkill(13, false);
			break;
	}
}

具体是如何封装的还是看项目中的具体代码吧…

2.ADAPTER(适配器) 关键词:转接[eg:电源转接头]

(1) 问题:

两个功能一致的类,但是由于接口的不同不能直接复用,加入适配器做接口转换。

(2)意图:

将一个类的接口转换成客户希望的另外一个接口。 A d a p t e r模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

(3)适用性:

• 你想使用一个已经存在的类,而它的接口不符合你的需求。
• 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接 口可能不一定兼容的类)协同工作。
• (仅适用于对象 A d a p t e r)你想使用一些已经存在的子类,但是不可能对每一个 都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
完美的实现了开闭原则,不会去改原代码

(4)结构:

在这里插入图片描述

(5)实例:

你的台灯插头是三孔的,而你家墙上的插座是两个孔,你需要一个插排来让你的台灯亮起来,这个插排就是适配器。

//实际不存在(也就是墙上并没有三孔插座)
class 期望目标{
        virtual public void 三个孔(){}
    }
    
//实际存在的
    class 墙上插座
    {
        public void 两个孔()
        {
            Console.WriteLine("有电,亮了");
        }
    }
    //适配器类怎么编写
    //1>创建适配器类
    //2>继承希望的类,重写希望类中方法
    //3>在适配器类中实例化已有的类;调用已有类的对象的方法
    class 转接头 : 期望目标
    {
        //创建已有的类
        private 墙上插座 adaptee = new 墙上插座();
        public override void 三个孔()
        {
            adaptee.两个孔();
        }
    }
    //调用
    public class Client
    {     
        public static void Main(string[] args)
        {
            转接头 obj = new 转接头();
            obj.三个孔();
            Console.Read();
        }
}
    

3.BRIDGE(桥接)关键词:连接两端的变化

(1)问题:

主体类有变化产生不同的派生,但是主体类成员也有变化,在组合关系上可能会形成排列组合,所以将两个变化分别独立,再用关联建立主体与成员的关系,这个关联即是桥

(2)意图:

将抽象部分(主体的成员)与它的实现部分分离,使它们都可以独立地变化。

(3)适用性:

• 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。 例如这种情况可能是因为,
在程序运行时刻实现部分应可以被选择或者切换。
• 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时 B r i d g e模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
• 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
• 正如在意图一节的第一个类图中所示的那样,有许多类要生成。这样一种类层次结构说
明你必须将一个对象分解成两个部分。 R u m b a u g h称这种类层次结构为“嵌套的泛化”
(nested generalizations) 。
• 你想在多个对象间共享实现(可能使用引用计数) ,但同时要求客户并不知道这一点。

(4)结构:

在这里插入图片描述

(5)案例:

a.见面问候:【问候 赞美 A场地 桥 B场地】
就像下面这四件事前两者(问候)可以和后两者(赞美)任意组合,随意发生
早晨 早上好 晚上 晚上好
新衣服 衣服漂亮 发型 发型好

b.画图:图形类: 圆形 矩形 输出位置类: 黑板 纸上 排列组合

public abstract class 输出位置类
{ 	
	public abstract void Position();
}
public class 黑板 : 输出位置类
{
    public override void Position(){ Console.WriteLine("输出位置类 黑板"); }
}
public class 纸上 : 输出位置类
{
    public override void Position(){ Console.WriteLine("输出位置类  纸上"); }
}
public abstract class 图形
{
    public 输出位置类 implemetor;
    virtual  public void Draw(){	implemetor.Position();}
}
public class 矩形 : 图形
{
	public override void Draw()
	{
		Console.WriteLine(" 图形 矩形 ");            
		implemetor.Position();
	}
}
public class 圆形 : 图形
{
	public override void Draw()
	{
		Console.WriteLine(" 图形 圆形 ");
		implemetor.Position();
	}
}


//调用
class Program
    {
        static void Main(string[] args)
        {
            //在纸上画圆形
            图形 obj1 = new 圆形();
            obj1.implemetor=new 纸上();
            obj1.Draw();
            //在黑板上画矩形
            图形 obj2 = new 矩形();
            obj2.implemetor = new 黑板();
            obj2.Draw();
            Console.Read();
        }
}

4.COMPOSITE(组合)关键词:树

(1)问题:

将对象组织成树形结构【eg:磁盘、公司组织】

(2)意图:

将对象组合成树形结构以表示“部分 -整体”的层次结构。C o m p o s i t e使得用户对单个对象和组合对象的使用具有一致性。

(3)适用性:

• 你想表示对象的部分 -整体层次结构。
• 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

(4)结构:

在这里插入图片描述

(5)我的理解:

就是把适合树形结构的类分成叶子类(上图Leaf)和父类(上图Composite),均继承自根类(上图Component),并且每个父类中都存有其包含的所有一级子类,对应插入Add删除Remove等各种方法。

(6)实例:
public abstract class EmployeeBase//公司(就是个根类)
{
	protected string strName;//名字
	public int salary;//薪水
	public EmployeeBase(string name, int salary)
	{
		strName = name;
		this.salary = salary;
	}
	abstract public void Add(EmployeeBase c);//添加一个元素
	abstract public void Add(EmployeeBase[] cs);//添加多个元素
	public abstract void DumpContents();//展示元素
	virtual public int GetSalary(){ return salary;}//获取薪水
}
public class Manager : EmployeeBase//CEO或者部门经理
{
	private ArrayList employees = new ArrayList();
	public Manager(string name, int salary) : base(name, salary) { }
	override public void Add(EmployeeBase c){ employees.Add(c);}
	public int Count{ get { return employees.Count; }}
	public override void DumpContents(){//部门经理或者CEO手下有人,都输出。
	Console.WriteLine("Node: {0}", strName);//经理或CEO
	foreach (EmployeeBase c in employees){ c.DumpContents();}//手下人
	public EmployeeBase GetChild(int i){ return (EmployeeBase)employees[i];}
}
public class Employee : EmployeeBase//员工
{
	public Employee(string name, int salary) : base(name, salary) { }
	override public void Add(EmployeeBase c)
	{
		Console.WriteLine("Cannot add to a leaf");
	}
	public override void DumpContents()
	{
		Console.WriteLine("Node: {0}", strName);
	}
}
public class Client//调用
    {  
        public static void Main(string[] args)
        {
//9个员工(叶子) 2+3+2+2
            Employee e1 = new Employee("z1", 10000);
            Employee e2 = new Employee("z2", 10000);
            Employee e3 = new Employee("z3", 10000);
            Employee e4 = new Employee("z4", 10000);
            Employee e5 = new Employee("z5", 10000);
            Employee e6 = new Employee("z6", 10000);
            Employee e7 = new Employee("z7", 10000);
            Employee e8 = new Employee("z8", 10000);
            Employee e9 = new Employee("z9", 10000);	
            //6个管理者(父类)
            Manager m1 = new Manager("CEO", 50000);
            Manager m2 = new Manager("市场经理", 30000);
            Manager m3 = new Manager("客服经理", 30000);
            Manager m4 = new Manager("研发经理", 30000);
            Manager m5 = new Manager("研发服务端组长", 20000);
            Manager m6 = new Manager("研发客户端组长", 20000);
            //建立层次关系
            m1.Add(m2); m1.Add(m3); m1.Add(m4);
            m2.Add(e1); m2.Add(e2);
            m3.Add(e3); m3.Add(e4);
            m4.Add(m5); m4.Add(m6);
            m5.Add(e5); m5.Add(e6);
            m6.Add(e7); m6.Add(e8); m6.Add(e9);
            //计算所有人员的薪水
            var totalSalary = GetAllSalary(m1);
            Console.WriteLine(totalSalary);
            Console.ReadLine();
        }
//计算emp及其所有手下的工资
        public static int GetAllSalary(EmployeeBase emp)
        {
            var salary = emp.GetSalary();
            if (emp is Manager)
            {
                var mgr = emp as Manager;
                for (int i = 0; i < mgr.Count;i++ )
                {
                    salary = salary + GetAllSalary(mgr.GetChild(i));                             
                }
            }
            return salary;
        }
}

5.DECORATOR (装饰)关键词:包装

(1)问题:

对一个对象动态扩展原有的行为能力,不断包装,不断的扩展

(2)意图:

动态地给一个对象添加一些额外的职责。就增加功能来说,D e c o r a t o r模式相比生成子类更为灵活。

(3)适用性:

• 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
• 处理那些可以撤消的职责。
• 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

(4)结构:

在这里插入图片描述

(5)我的理解

在原有类的基础上,可能会有很多属于一种性质但是不同方式的功能,就比如下面那个实例,加装饰到武器上,装饰可能有很多种,各自的功能也不同,我们就可以把所有的装饰都归为1类,具体功能到具体装饰类中再实现,而武器就会作为装饰的一个属性,很方便。

(6)实例:
abstract class 武器
{
	public abstract void 伤害();
}
class 宝剑 : 武器
{       
	public override void 伤害()
	{
		Console.WriteLine("宝剑基本行为 -伤害");
	}
}
abstract class 装饰器 : 武器
{
	public 武器 武器对象{get;set;}
	public 装饰器(武器 c){ 武器对象 = c;}
	public override void 伤害()
	{
		if (武器对象 != null)	武器对象.伤害();
	}
	public 武器 拆装饰(){ return 武器对象;}
}
class 红宝石 : 装饰器
{
	public 红宝石(武器 武器对象) : base(武器对象){ }
	public override void 伤害()
	{          
		base.伤害();
		眩晕();
	}
	private void 眩晕()
	{
		Console.WriteLine(" 红宝石附加功能:眩晕....");
	}
}
class 蓝宝石 : 装饰器
{
	public 蓝宝石(武器 武器对象): base(武器对象){ }
	public override void 伤害()
	{
		base.伤害();
		冰冻();
	}
	private void 冰冻()
	{
		Console.WriteLine("蓝宝石附加功能:冰冻....");
	}
}
public class Client//调用
{
	public static void Main(string[] args)
	{
		武器 wq = new 宝剑();
		wq.伤害();// => 宝剑基本行为 -伤害
		武器 wq2 = new 红宝石(new 蓝宝石(wq));
		wq2.伤害(); // => 宝剑基本行为 -伤害	
		蓝宝石附加功能:冰冻.... 
		红宝石附加功能:眩晕....
		//如何拆装备(平时怎么脱衣服这里就是怎么拆的)
		wq2 = (wq2 as 装饰器).拆装饰();
		wq2 = (wq2 as 装饰器).拆装饰();
		wq2.伤害();// => 宝剑基本行为 -伤害
		Console.ReadKey();
	}
}

6.PROXY(代理)关键词(幌子)

(1)问题:

对一个对象进行访问控制的一个原因是为了只有在我们确实需要这个对象时才对它进行创建和初始化,在创建初始化之前,由一个小对象表示这个大对象。

(2)意图:

为其他对象提供一种代理以控制对这个对象的访问。

(3)适用性:

在需要用比较通用和复杂的对象指针代替简单的指针的时候,
使用Proxy模式。下面是一些可以使用Proxy模式常见情况:
远程代理(Remote Proxy)为一个对象在不同的地址空间提供局部代表
虚代理(Virtual Proxy)根据需要创建开销很大的对象。
保护代理(Protection Proxy )控制对原始对象的访问。保护代理用于对象应该有不同
的访问权限的时候。
智能指引(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。
当第一次引用一个持久对象时,将它装入内存。
在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

(4)结构:

在这里插入图片描述

其实委托已经完美地提供了这个功能,同样都是代理,显然用微软给我们的这个功能要比自己写方便许多,所以看看就行了。

四、行为型-关注对象交互的问题

1.TEMPLATE METHOD( 模板方法) 关键词(骨架)

(1)问题:

对于某功能实现的大流程已经确定,但是每一个步骤可能有不同的实现。在父类中定义流程(骨架)子类负责每个步骤的具体实现.

(2)意图:

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。 TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

(3)适用性:

• 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
• 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是O p d y k e和J o h n s o n所描述过的“重分解以一般化”的一个很好的例子 [ O J 9 3 ] 。首先识别
现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
• 控制子类扩展。模板方法只在特定点调用“ h o o k”操作(参见效果一节) ,这样就只允许在这些点进行扩展。

(4)结构:

在这里插入图片描述

说白了,就是把他们共同性质的方法都写在抽象类中,由于做法不同,在他们具体的类中重写各自做法的方法即可。

2.STRATEGY(策略) 关键词(切换算法)

(1)问题:

算法是多种实现,但是实际使用中,往往选择其中之一,且可以在运行时切换不同的算法,看上去就像动态改变一个方法的执行

(2)意图:

定义一系列的算法 ,把它们一个个封装起来 ,并形成一个一致的抽象,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。说白了,做同一件事但是做的方式不同。

(3)适用性:

• 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一
个类的方法。
• 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 / 时间权衡的
算法。当这些变体实现为一个算法的类层次时 [ H O 8 7 ] ,可以使用策略模式。
• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数
据结构。
• 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。
将相关的条件分支移入它们各自的 S t r a t e g y类中以代替这些条件语句。

(4)结构:

在这里插入图片描述

(5)实例

软件公司有程序员,代码测试人员…,现在需要计算他们的薪水,但他们的薪水计算方式不同,这是我们就可以使用策略设计模式。

abstract class StrategySalary//薪水计算算法抽象类
{
	private int baseSalary;//基本工资
	private int subsidy;//补助
	public StrategySalary(int baseSalary, int subsidy)
	{
		this.baseSalary =baseSalary;
		this.subsidy = subsidy;
	}
	virtual public int GetSalary()//获取薪水的基方法
	{
		return baseSalary + subsidy;
	}
}
class ProgramerStrategySalary : StrategySalary//程序员类
{
	private int projectBonus;//项目提成
	public ProgramerStrategySalary(int baseS,int subS) : base(baseS, subS){ }
	public int ProjectBonus//可以通过上面的构造方法获得属性值,也可以通过属性
	{
		set { projectBonus = value; }
	}
	override public int GetSalary()//程序员薪水的计算方法
	{
		return base.GetSalary() + this.projectBonus;
	}
}
class TesterStrategySalary : StrategySalary//代码测试人员类
{
	private int bugs;//找出的bug数
	public int Bugs//通过属性获取
	{
		get { return bugs; }
		set { bugs = value; }
	}
	public TesterStrategySalary(int baseS, int subS): base(baseS,subS){ }
	override public int GetSalary()//代码测试人员类薪水的计算方法
	{
		return base.GetSalary() + bugs * 200;
	}
}

至此关于薪水的计算已经完成了,我们再用一个员工类去进行具体员工的实例化,这样完美的封装了上面的设计和算法。

class Employee //员工类(在这里面去选择员工类型并计算其薪水(调用))
{
	private string job;
	public string Job
	{
		get { return job; }
		set { job = value; }
	}        
	StrategySalary strategy;
	public Employee(StrategySalary strategy)
	{
		this.strategy = strategy;
	}
	public int GetSalary()
	{
		return  strategy.GetSalary();
	}
	public void DoWork()
	{
	// some of the context's own code goes here
	}
}
public class Client//调用
    {
        public static void Main(string[] args)
        {
//这里直接实例化了,在实际中可能类别很多我们可以结合工厂类的方式去调			  用不同策略
            ProgramerStrategySalary strategy =new ProgramerStrategySalary(10000, 2000);
            Employee c = new Employee(strategy);
            strategy.ProjectBonus = 10000;
            c.Job = "程序员";
            int re=c.GetSalary();
            Console.WriteLine( c.Job+":薪水"+re);
            Console.ReadKey();
        }
}

3.STATE(状态)关键词:状态决定行为

(1)问题:

对象状态的改变,从而带来行为上的变化。

(2)意图:

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类

(3)适用性:

• 一个对象的行为取决于它的状态 , 并且它必须在运行时刻根据状态改变它的行为。
• 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状
态通常用一个或多个枚举常量表示。通常 , 有多个操作包含这一相同的条件结构。 S t a t e模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对
象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化

(4)结构:

在这里插入图片描述

但是这里只说了状态,实际中还往往伴随着各种各样的条件,可以看AI中的内容。

(5)实例:显然这种随状态改变行为的事我们已经做了很多次了。

练习1:实现瓶子的倒水的功能,包含关闭,打开状态,要求能方便切换
步骤1:定义枚举
2:定义状态抽象基类,实现状态子类【多个】
3:定义状态机-瓶子
1》要确定当前状态 并初始化
2》定义状态切换的方法
3》调用当前状态的方法
4》根据需要定义其它成员

    abstract class State
    {
        protected string strStatename;//状态名
        abstract public void Pour();//倒水
        // do something state-specific here
    }
    class OpenedState : State//打开状态
    {
        public OpenedState()
        {
            strStatename = "Opened";
        }
        override public void Pour()//开着的时候可以倒水
        {
            Console.WriteLine("...pouring...");
            Console.WriteLine("...pouring...");
            Console.WriteLine("...pouring...");
        }
    }
    class ClosedState : State//关闭状态
    {
        public ClosedState()
        {
            strStatename = "Closed";
        }
        override public void Pour()//关着的时候倒不了
        {
            Console.WriteLine("ERROR - bottle is closed - cannot pour");
        }
    }
    class ContextColaBottle //瓶子状态机
    {
        public enum BottleStateSetting //状态枚举
        {
            Closed,
            Opened
        };
         // If teh state classes had large amounts of instance data,
        // we could dynamically create them as needed - 
        //if this demo they are tiny, so we just  create them as data members
        //OpenedState openedState = new OpenedState();
        //ClosedState closedState = new ClosedState();

        
public ContextColaBottle()
        {
            // Initialize to closed
            CurrentState = closedState;
        }
        private State CurrentState;//当前状态

        public void SetState(BottleStateSetting newState)//设置状态
        {
            //if (newState == BottleStateSetting.Closed)
            //{
               // CurrentState = closedState;
            //}
            //else
            //{
               // CurrentState = openedState;
            //}
			//在最开始的英文注释中也说了,当实例化对象过多时就不能全部new出来了,而应该使用动态创建,也就是用下面的反射,而且放在这个设置状态的方	法中更加方便。
			string allName = "State_DesignPattern2." + newState + "State";
			Type typeObj = Type.GetType(allName);          
			CurrentState = (State)Activator.CreateInstance(typeObj); ;
        }
        public void Pour()
        {
            CurrentState.Pour();
        }
    }
    public class Client//调用
    {
        public static int Main(string[] args)
        {
            ContextColaBottle contextColaBottle = new ContextColaBottle();
            Console.WriteLine("initial state is closed");
            Console.WriteLine("Now trying to pour");
            contextColaBottle.Pour();
            Console.WriteLine("Open bottle");
            contextColaBottle.SetState(ContextColaBottle.BottleStateSetting.Opened);
            Console.WriteLine("Try to pour again");
            contextColaBottle.Pour();
            return 0;
        }
    }

4.OBSERVER(观察者)

(1)问题:

一个对象的改变,及时的将通知广播给其他关注它的对象,(一对多的通知)

(2)意图:

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时 , 所有依赖于它的对象都得到通知并被自动更新

(3)适用性:

• 当一个抽象模型有两个方面 , 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
• 当对一个对象的改变需要同时改变其它对象 , 而不知道具体有多少对象有待改变。
• 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之 , 你不希望这些对象是紧密耦合的。

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 设计模式游戏与完美开发 PDF 是两个不同的东西,其设计模式游戏是一本介绍常用设计模式的书籍,而完美开发 PDF 是一种开发技巧与知识总结的文档。 设计模式游戏适合那些想更深入了解设计模式的软件工程师、程序设计师或学习设计模式的学生。这本书详细介绍了23种常见的设计模式,并通过游戏方式帮助读者更好地理解和掌握这些模式的应用。设计模式可以提高代码的可读性、可维护性和可扩展性,是面向对象编程的重要思想。 完美开发 PDF 则适合那些想提升自己开发技巧和知识的人员。这份文档详细总结了一些软件开发的注意事项和技巧,如代码规范、调试技巧、内存管理、测试等。这些知识和技巧可以帮助开发人员写出高质量、高效率的代码,提高开发效率和质量。 总的来说,这两个东西都是非常有价值的,但需要针对自己的需求选择合适的。设计模式游戏可以帮助读者掌握常用设计模式,提高自己的编程思维和能力,而完美开发 PDF 则可以帮助读者提高开发能力和效率,写出更加高质量的代码。 ### 回答2: 设计模式游戏是一个基于游戏框架的学习设计模式的互动式教程。该游戏涵盖了常见的设计模式,通过解决问题,并用代码实现来学习设计模式游戏的角色扮演和动画效果增加了游戏的趣味性和互动性,从而更容易留下深刻的理解。设计模式游戏是一个非常有效的学习工具,使得学习设计模式更加生动和有趣。 而完美开发 pdf 则是一本以实践为主的技术书籍,它详细介绍了软件开发的各个环节,包括需求分析、设计、代码实现、测试、维护等。该书以项目驱动的方式,给出了概念性介绍和实际的代码实现,使得读者可以了解到整个软件开发流程,并获得实际的代码编写技巧和实践经验。完美开发 pdf 是一本非常实用的技术书籍,适合那些想要学习软件开发和完整项目开发的人员。 两者相比较,设计模式游戏更注重提高学习者的兴趣和理解,而完美开发 pdf 更注重实际操作和项目实践,两者均是非常有效的学习工具和实践指南,适合不同层次的软件工程师和学习者。 ### 回答3: 设计模式游戏是一款面向软件开发者的教育游戏,其目的是通过互动式的游戏流程,让学习者深入了解和掌握设计模式的原理、应用和实现。通过该游戏的教育模式,学习者可在不断试错和调整的过程,逐渐领会设计模式的实际意义和作用,有效提升自身的软件设计和开发能力。相比传统的讲授式教育模式,设计模式游戏更注重学习者的实践和探索,提升学员的参与度和自主学习能力。 而完美开发 pdf 则是一本软件开发者必备的技术读物,它以设计模式为基础,深入解析了软件开发的各个方面,涉及到的知识点非常全面而深入。它主要从实践的角度出发,以案例为驱动,通过实例和代码的演示,让读者系统地了解和掌握软件设计和开发的常见问题和解决方案。作为一本非常经典的开发读物,完美开发 pdf 具有指导性和启发性,可让软件开发者在实践逐渐成长和完善自身的工作和技能。 因此,通过设计模式游戏与完美开发 pdf 的学习,软件开发者可在掌握设计模式的同时,更全面地了解和掌握软件开发的各个方面,从而在实践不断成长和提升自身的技术水平。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值