• Unity3d与设计模式(三)工厂模式


    这个系列的文章。并不会将全部用到的设计模式全部讲一遍。其实我个人觉得,并非全部的设计模式都适用于unity3d。这里讲的主要还是一些经常使用的设计模式。


    那么,本章讲的就是常见的构建型模式其中的工厂模式。

    简单工厂模式

    讲工厂,首先得从简单工厂说起。


    简单工厂模式的目的是用来创建不同类型的对象。须要指出的是它并非GOF的23种模式之中的一个。

    结构

    类图

    实现

    废话少说,直接上代码。

    public interface IProduct {
        void DoSth();
    }
    
    public class ProductFirst : IProduct {
        public virtual void DoSth(){
            Debug.Log("ProductFirst DoSth");
        }
    }
    public class ProductSecond : IProduct {
        public virtual void DoSth(){
            Debug.Log("ProductFirst DoSth");
        }
    }
    
    public class SimpleFactory {
        public static IProduct Create(int id){
            switch(id){
                case 1:
                    return new ProductFirst();
                    break;
                case 2:
                    return new ProductSecond();
                    break;
                default:
                    return null;
                    break;
            }
        }
    }

    简单工厂模式的Create()方法里,能够加入各种逻辑,用于创建相应的实例。unity3d中非常多时候创建的是游戏中的物件,这时简单工厂模式中创建者的參数能够相应prefab的名字。

    长处

    • 简单。能够取名叫『2分钟内能够学会的设计模式』
    • 实现逻辑清晰。依据不同的创建參数创建相应实例。

    名为简单工厂方法,看起来果然是非常简单,对不正确?那么。本着”simple is best”的逻辑,是不是我们应该大力推广简单工厂模式呢?
    答案是「No」。简单工厂模式有其固有的缺陷,在使用时须要严格限定其范围。

    缺陷

    让我们首先考虑一个问题。此处使用的Create()方法。直接决定我们产生实例的逻辑。
    那么。如今问题来了。
    假如我们不希望通过推断參数是1还是2。来进行不同实例的生成呢?
    显然,一旦我们须要新的逻辑来产生实例的话。我们就不得不正确代码进行改动。
    当然。从理论上,我们也能够发现简单工厂模式的一些问题。
    Open-closed原则。即是对扩展开放。对改动封闭。

    使用简单工厂模式时,非常多时候违背了这一原则。

    同一时候。因为产生不同实例的方法在可预见的将来有可能会变得非常复杂,是否满足单一职责这一点也值得商榷。


    那么,我们有办法解决问题吗?嗯,接下来就是抽象程度更高的方法出场了。

    工厂方法

    工厂方法与简单工厂最大的差别。在于工厂方法将工厂进行了抽象,将实现逻辑延迟到工厂的子类。

    结构

    工厂方法

    实现

    为了让我们样例更贴近生产环境,在这里採用一个更加实际的问题。
    场景其中有两个物体。我们希望其中一个向左走,一个向右走。
    我们用工厂方法来生成这两个向左向右的控制器。并加入到相应的物体上。当然。其实这个样例依旧非常单薄,实际面对这个问题我们肯定不会这样实现就是了。
    上代码
    * IWalker

    public interface IWalker {
        void Walk(Transform target);
    }
    • LeftWalker
    public class LeftWalker : MonoBehaviour, IWalker {
        Transform _target;
        public virtual void Walk(Transform target){
            _target = target;
            StartCoroutine(WalkLeft());
        }
    
        IEnumerator WalkLeft(){
            while(true){
                _target.Translate(Vector3.left * Time.deltaTime);
                Debug.Log("WalkLeft " + _target.localPosition);
                yield return new WaitForFixedUpdate();
            }
        }
    }
    • RightWalker
    public class RightWalker : MonoBehaviour, IWalker  {
        Transform _target;
        public virtual void Walk(Transform target){
            _target = target;
            StartCoroutine(WalkRight());
        }
    
        IEnumerator WalkRight(){
            while(true){
                _target.Translate(Vector3.right * Time.deltaTime);
                Debug.Log("WalkRight " + _target.localPosition);
                yield return new WaitForFixedUpdate();
            }
        }
    }
    • IWalkerFactory
    public interface IWalkerFactory {
        IWalker Create();
    }
    
    • LeftWalkerFactory
    public class LeftWalkerFactory : IWalkerFactory {
        public virtual IWalker Create(){
            return new GameObject().AddComponent<LeftWalker>();
        }
    }
    • RightWalkerFactory
    public class RightWalkerFactory : IWalkerFactory{
        public virtual IWalker Create(){
            return new GameObject().AddComponent<RightWalker>();
        }
    }

    长处

    工厂方法比简单工厂多了一层抽象。
    因为抽象工厂层的存在。当我们须要改动一个实现的时候,我们不须要改动工厂的角色,仅仅须要改动实现的子类就能够完毕这个工作。
    相同。当我们须要添加一个新产品的时候,我们也不须要改动工厂的角色,仅仅须要添加一个新的实现工厂来完毕实现就能够了。


    显然,这样更易于扩展。并且,总体代码的层级结构更加分明,创建实际产品的职责更加单一。
    此外。非常显然客户在定义工厂角色的时候不须要知道实现子类。仅仅有当实际须要创建的时候,才动态指定子类。这相同带来了代码的稳定性和最小可知性。

    缺陷

    显然,使用工厂方法的代码量是多于简单工厂的。
    同一时候,每添加一个新的产品,就会添加一个新的工厂类,代码的复杂程度自然也随之上升了。我们会为此创建非常多的工厂。

    抽象工厂

    抽象工厂和工厂方法实际上是非常像的,只是抽象工厂添加了另外一个概念。就是产品族。也就是说,一个工厂能够生产一系列的产品,这些产品的定义都在工厂其中。

    结构

    这里写图片描写叙述

    实现

    ok。这个模式老实说意义不是非常大。直接上代码吧,就不加凝视了

    public interface IActorFactory  {
        IFlyer CreateFlyer(GameObject go);
        IWalker CreateWalker(GameObject go);
    }
    
    public interface IFlyer {
        void Fly(Transform target);
    }
    
    public class LeftActorFactory : IActorFactory {
        public virtual IFlyer CreateFlyer(GameObject go){
            return go.AddComponent<LeftFlyer>();
        }
    
        public virtual IWalker CreateWalker(GameObject go){
            return go.AddComponent<LeftWalker>();
        }
    
    }
    
    public class RightActorFactory : IActorFactory{
        public virtual IFlyer CreateFlyer(GameObject go){
            return go.AddComponent<RightFlyer>();
        }
    
        public virtual IWalker CreateWalker(GameObject go){
            return go.AddComponent<RightWalker>();
        }
    
    }
    

    长处

    当我们须要添加一个产品族的时候,我们仅仅须要添加一个工厂,实现其中全部产品的实现即可了。
    抽象工厂的设计,使得我们能够非常easy的添加一个产品系列。

    缺点

    抽象工厂其中,产品族的定义使得子类必须去实现全部的产品生产。
    因此。抽象工厂并不适合于横向扩展。即须要添加产品的情况。
    一旦须要添加产品,那么我们甚至须要去改动抽象的基类。这是比較违反开闭原则,不太符合面向对象设计的做法。

    总结

    从简单工厂到工厂方法再到抽象工厂。

    我们能够看到,抽象的程度越来越高,能够解决的问题也越来越复杂。
    只是,个人的经验而言,一般在unity3d其中也顶多用到工厂方法而已。抽象工厂其实并非一个非常灵活的解决方式。
    并且,对于unity3d中组件的创建,其实是有一些非常灵活的解决方式能够处理的。实体与组件系统。相当适合于组件的构建,比起工厂方法来说更加灵活和易于扩展。


    以后有时间的时候再对此进行说明。

    本章的样例
    工厂模式

  • 相关阅读:
    详解IP地址、子网掩码、网络号、主机号、网络地址、主机地址
    K8Syaml使用认证镜像仓库配置
    后端——框架——视图层框架——spring mvc——View & ViewResolver
    11.MongoDB oplog窗口时间
    1.Mongodb的监控
    MongoDB 4.4.12找不到mongostats命令
    10.Mongod安装后找不到mongostat等命令
    4.怎样正确的关闭和重启集群
    5.oracle打optch报错:Inventory load failed... OPatch cannot load inventory for the given Oracle Home
    1.linux高手文章
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7220737.html
Copyright © 2020-2023  润新知