Unity3D 设计模式学习之建造者模式

前言

建造者模式Builder在书中用来对于角色的组装,角色组装可以使用工厂模式来生存角色对象,但是如果要对
角色(双方士兵)作不同差异功能的时候就要再次分开写两个新的工厂类(我方士兵工厂类,敌方士兵工厂类),如果要对士兵类添加新的功能,则可以往工厂类添加方法。

但是这样做会让工厂管理类

这时可以用到建造者模式了
GOF对建造者模式的定义是:
将一个复杂对象的构建流程与它的对象表现分离出来,让相同的构建流程可以产生出不同的对象行为的表现

比如说,配置一辆车子时,都会按照一定的步骤来组装

准备车架-> 外观烤漆 -> 将引擎放入车架 -> 装入内装椅
此时就把组装的流程定义好了,但是具体的需求完全可以由我们来设定

建造者模式


建造者模式可以分两个步骤来实施
1、将赋值的构建流程独立出来,并将整个流程分成几个步骤,其中的每一个步骤可以是一个功能组件的设置,也可以是参数的指定,并且在一个构造方法中,将这些步骤串接起来

2、定义一个专门实现这些步骤(提供这些功能)的实现者,这些实现者知道每一部分该如何完成,并且能接受参数来决定要产出的功能,但不知道整个组装的流程是什么。

流程分析安排,功能分开实现,就能让建造者模式应用在复杂的对象构建流程上。

此时我们需要知道一些术语:
1、Director(建造指示者)
负责对象构建时的流程安排
该类有一个方法,明确定义对象组装的流程,即调用Builder接口方法的顺序

2、Builder( 功能实现接口 )
定义不同的操作方法将“功能分开来实现”
其中每一个方法都是用来提供给某复杂对象的一部分功能,或是提供设置规则

3、ConcreteBuilder( 功能实现者 )
Builder的具体实现,实现产出功能的类。
不同的功能实现者可以产出不同的功能,用来实现不同对象的行为表现功能

4、Product产品
代表最终完成的复杂对象,必须提供方法让Builder类可以将各部分功能设置给它

实现范例
public class Director{
    private Product m_product;
    public Director(){}
    //构建方法
    public void Construct(Builder theBulider) {
        m_product = new Product();
        theBulider.BuilderPart1(m_product);
        theBulider.BuilderPart2(m_product);
    }
    public void Product GetProduct(){
        return m_product;
    }
}
//建造者接口,确定实现大致功能
public abstruct class Bulider{
    public void abstruct void BuilderPart1(Product theProduct);
    public void abstruct void BuilderPart2(Product theProduct);
}
//子类来实现接口所需的方法,不同的子类可以定义不同属性的功能
//产出的功能直接传入给theProduct对象

public class ConcreateBuilderA : Builder {
    public override void BuildPart1(Product theProduct) {
        theProduct.AddPart("ConcreateBuilderA_part1");
    }
    public override void BuildPart2(Product theProduct) {
        theProduct.AddPart("ConcreateBuilderA_part2");
    }
}
public class ConcreateBuilderB : Builder {
    public override void BuildPart1(Product theProduct) {
        theProduct.AddPart("ConcreateBuilderB_part1");
    }
    public override void BuildPart2(Product theProduct) {
        theProduct.AddPart("ConcreateBuilderB_part2");
    }
}

public class Product{
    // 产品类就需要对数据进行封装,写一些公有Set方法,让Builder的方法来为Product类设置属性字段对象
    private List<String> m_Part = new List<String>();
    public Product(){}
    public void AddPart(string part){
        m.Part.Add(part);       
    }
    public void ShowProduct(){
        foreach(string part in m_Part){
            Debug.Log(Part);
        }
    }
}

//获取流程
void UnitTest(){
    Director theDirector = new Director();
    Product  theProduct = null;

    //使用BuilderA构造
    theDirector.Construct(new ConCreateBuilderA());
    theProduct = theDirector.GetResult();
    theProduct.ShowProduct();

    theDirector.Construct(new ConCreateBuilderB());
    theProduct = theDirector.GetResult();
    theProduct.ShowProduct();

}

在Director建造指示者的指挥下,将不同属性的功能指定给Product,最后获取Product。
可以看到,Product对象在使用不同的Builder(功能实现者)时,会有不同的功能表现。

角色组装

按照建造者模式的两个原则:流程分析安排,和功能分开实现

从原先的实现中可以发行有一些是可以有固定的流程的

如给角色对象添加功能的流程方法
AddCharacterFuncs() {
设置显示模式
AddGameObject()
设置武器
AddWaePon()
设置角色属性
AddAttr()
设置角色AI
AddAI()
}
上述就是有固定的流程安排

而Solider 角色工厂(SoldierFactory) 和 Enemy 敌人工厂(EnemeyFactory)两个工厂方法则是功能分开实,现此时就将建造者模式找到;
但是如果在工厂上实现,则会出现前言所说的情况,增加工厂类就需要对(士兵和敌方士兵)进行重新实现,出现继承绑定的情况

所以必须对这部分流程安排从角色工厂中分离,单独实现成为一个新的系统

角色建造者系统

角色建造者系统


角色建造者系继承游戏系统接口,定义一个ConStruct方法,方法传入某个Builder的对象,方法里面指定了所需要的流程。类似于上述的Director指示者类

定义抽象类接口
1、使用一个角色建造函数参数属性抽象类,里面定义了角色所需要建造的类型对象,字段属性
使用角色构造函数类是因为当一个角色有过多的属性(7 、8 个)时,此时就传入一个专门的函数参数类,会方便后续的维护和开发

2、定义一个角色建造者抽象类,里面包含了角色大致的方法。(让子类继承做具体不同的实现)

具体实现
此时我们需要具体实现一个角色建造参数类,还可以添加上一些参数类进行扩展
如给士兵

public class SoldierBuildParam:ICharacterBuildParam {
    public int LV = 0; //派生类新增的属性
    public SolderBuildParam();
}

public class SoldierBuilder : ICharacterBuilder{
    private SoldierBuildParam m_BuilderParam = null;
    public override void SetBuildParam(ICharacterBuildParam theParam) {
        m_BuildParam = theParam as SoldierBuildParam;
    }
    //设置模型
    public override void LoadAsset(int GameObjectID){
        //设置角色属性模型
        m_BuildParam.NewCharacter.SetGameObject(xxx);
    }
    //加入点击组件功能
    public override void AddOnClickScript(){
        //具体实现.. 设置角色属性
    }
    //加入AI
    public override void AddAI(){
        //具体实现..给m_BuildParam 设置AI 
    }
}

工厂类重构

此时工厂类有一个建造指示者对象引用,来指示设置流程调用ConStrauct方法,方法参数为传入角色建造者对象(theBuilder 士兵或敌人的建造者)。

在调用ConStrauct方法之前,需要设置一个角色函数参数属性对象引用,来设置基本的参数,并且传给theBuilder对象;theBuilder对象拥有属性参数时,在调建造指示者的ConStrauct方法,传入有属性类对象引用的theBuilder。

对于工厂类里的另一个敌人生成工厂模式方法也一样,定义一个敌人参数属性类,并通过工厂模式方法传入的参数来设置属性;然后定义一个theEnemyBuilder建造者 获取这个敌人参数

这样重构就不需要写士兵工厂,敌人工厂这两个工厂子类进行特定实现了。只需在工厂方法上添加建造指示者类对象引用,特定的建造者类,特定的建造者属性参数类。

重构后的角色工厂:

public class CharacterFactory:IcharacterFactory{
    private CharaterBuilderSystem m_BuilderDirector
     = new CharacterBuilderSystem(PBaseDefenseGame.Instance);

    public override ISoidier CreateSoldier(ENUM_Soldier emSoldier,ENUM_Weapon emWeapon,int Lv,Vector3 SpawnPosition){
        //产生对应的Character
        switch(emSoldier){
        case ENUM_Soldier.Rookie:
            SoldierParam.NewCharacter = new SoldierRookie();
        break;
        case ENUM_Soldier.Sergeant:
            SoldierParam.NewCharacter = new SoldierSergeant();
        break;
        //...
        default :
            Debug.LogWarning("CreatteSoldier:无法建立");
            return null;
        }

        if(SoldierParam.NewCharacter == null)
        return null;

        //设置共享参数
        SoldierParam.emWeapon = emWeapon;
        SoldierParam.SpawnPosition = SpawnPosition;
        SoldierParam.Lv = Lv;

        //产生对应的Builer及设置参数
        SoldierBuilder theSoldierBuilder = new SoldierBuilder();
        theSoldierBuilder.SetBuildParam(SoldierParam);
        m_BuilderDiector.Construct(theSoldierBuilder);

        return SoldierParam.NewCharacter as ISoldier;

    }

    //敌人工厂方法类似在此省略...
}
使用建造者模式的优点

在重构后的角色工厂中,只简单负责角色的产生,而负责的功能组装工作交由新增加的角色建造者系统来完成。

运用建造者模式的角色建造者系统,将角色功能的组装流程给独立出来,并以明确的方法调用来实现,有助于代码的阅读和维护;而各个角色的功能装备人物,也交由不同的类来实现。并使用接口方法操作,降低系统之间的耦合度。当实现系统有任何变化时,也可以使用实现类的方式来应付。

总结

建造者模式让复杂的对象的生产流程与功能实现进行拆分,让系统调用和维护变得更加容易。
此外,在不需要更新实现者的情况下,调整生产流程的顺序就能完成装备线的更改。

猜你喜欢

转载自blog.csdn.net/liaoshengg/article/details/82078997