原型模式(创建型模式)

1、原型模式解决的问题

现在有一个抽象的游戏设施建造系统,负责构建一个现代风格和古典风格的房屋和道路.

前提:抽象变化较慢,实现变化较快(不稳定)

整个抽象的游戏设施建造系统相对变化较慢,本例中只有一个Build的创建方法,而Build内部的方法实现,该实现依赖与各种具体的实现,而这些实现变化的非常频繁,现在虽然只有现代风格和古典风格的房屋和道路的构建,而将来可能会卡通风格、另类风格等各种各样的对象加入到Build方法中来渲染游戏的背景.

在不考虑第三方容器组件(如Unity)和设计模式的情况下,为了快速完成这个任务,我们通常会用以下这种方式编码,代码如下:

    #region 抽象A
    /// <summary>
    /// 抽象的游戏设施建造系统
    /// </summary>
    public class BuildSystem
    {
        /// <summary>
        /// Build方法的逻辑变化较慢(只需要创建2种风格的房屋和道路,总共8个对象),但是风格变化较快,由于需求变化,可能需要创建诸如卡通风格、另类风格等的房屋和道路
        /// </summary>
        public void Builld()
        {
            ModernHouse modernHouseA = new ModernHouse();
            ModernHouse modernHouseB = new ModernHouse();
            ModernRoad modernRoadA = new ModernRoad();
            ModernRoad modernRoadB = new ModernRoad();
            ClassicalHouse classicalBuildA = new ClassicalHouse();
            ClassicalHouse classicalBuildB = new ClassicalHouse();
            ClassicalRoad classicalRoadA = new ClassicalRoad();
            ClassicalRoad classicalRoadB = new ClassicalRoad();
            //下面是具体的对象实例操作,如现代化房屋虽然有两个实例,但是可能两个可能高矮、外形不同等
        }
    } 
    #endregion

    #region 实现细节b
    /// <summary>
    /// 现代风格的房屋
    /// </summary>
    public class ModernHouse { }

    /// <summary>
    /// 现代风格的道路
    /// </summary>
    public class ModernRoad { }

    /// <summary>
    /// 古典风格的房屋
    /// </summary>
    public class ClassicalHouse { }

    /// <summary>
    /// 古典风格的道路
    /// </summary>
    public class ClassicalRoad { } 
    #endregion

从oop的角度分析上面的代码,可以理解为抽象的游戏系统直接依赖具体的实现细节(现代风格和古典风格的房屋和道路),如下图:

这时客户端的调用代码如下:

    /// <summary>
    /// Prototype原型模式-创建型模式
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            BuildSystem buildSystem = new BuildSystem();
            buildSystem.Builld();
        }
    }

这种设计方式的弊端显而易见,Build方法显得很无力,这个时候增加了一个新的需求,如下:

客户端需要构建一种卡通风格和另类风格的道路和房屋,但是Build方法的主逻辑还是不变,同样是(创建两种风格的房屋和道路,共8个对象).

这时Build方法显得很无力,只能创建一种特定逻辑的游戏背景建筑.(当然你可以在BuildSystem中新添一种新的Build方法来满足需求,但是这种方式的代码的重用性差)而且,掉到了,抽象依赖于实现的坑里面去了,这个时候我们就需要对代码进行重构,进行依赖倒置.如下图:

对所有的Build方法中的8个实例(实现细节b)进行抽象,让它们依赖于抽象B,让Build方法(抽象A)也依赖于抽象B,代码如下:

    #region 抽象A
    /// <summary>
    /// 抽象的游戏设施建造系统
    /// </summary>
    public class BuildSystem
    {
        /// <summary>
        /// Build方法的逻辑变化较慢(只需要创建2种风格的房屋和道路,总共8个对象),但是风格变化较快,由于需求变化,可能需要创建诸如卡通风格、另类风格等的房屋和道路
        /// </summary>
        public void Builld(House houseone, House houseTwo,Road roadone, Road roadtwo)
        {
            House modernHouseA = houseone.Clone();
            House modernHouseB = houseone.Clone();
            Road modernRoadA = roadone.Clone();
            Road modernRoadB = roadone.Clone();
            House classicalBuildA = houseTwo.Clone();
            House classicalBuildB = houseTwo.Clone();
            Road classicalRoadA = roadtwo.Clone();
            Road classicalRoadB = roadtwo.Clone();
            //下面是具体的对象实例操作,如现代化房屋虽然有两个实例,但是可能两个可能高矮、外形不同等
        }
    }
    #endregion

    #region 抽象B
    /// <summary>
    /// 抽象房屋
    /// </summary>
    public abstract class House
    {
        /// <summary>
        /// 抽象的House的Clone方法,用于构建House的多个实例,如果抽象A只需要一个实现b的一个实例,则不需要该方法
        /// </summary>
        /// <returns></returns>
        public abstract House Clone();
    }

    /// <summary>
    /// 抽象道路
    /// </summary>
    public abstract class Road
    {
        /// <summary>
        ///  抽象的Road的Clone方法,用于构建Road的多个实例,如果抽象A只需要一个实现b的一个实例,则不需要该方法
        /// </summary>
        /// <returns></returns>
        public abstract Road Clone();
    }
    #endregion

    #region 实现细节b
    /// <summary>
    /// 现代风格的房屋
    /// </summary>
    public class ModernHouse : House
    {
        public override House Clone()
        {
            //实现ModernHouse的浅拷贝,如果当前对象中含有数组等,则需要使用序列化的方式(深拷贝)实现对象的克隆,否则当一个对象实例修改了数组,另一个对象实例会共享该数组
            return (ModernHouse)MemberwiseClone();
        }
    }

    /// <summary>
    /// 现代风格的道路
    /// </summary>
    public class ModernRoad : Road
    {
        public override Road Clone()
        {
            return (ModernRoad)MemberwiseClone();
        }
    }

    /// <summary>
    /// 古典风格的房屋
    /// </summary>
    public class ClassicalHouse : House
    {
        public override House Clone()
        {
            return (House)MemberwiseClone();
        }
    }

    /// <summary>
    /// 古典风格的道路
    /// </summary>
    public class ClassicalRoad: Road
    {
        public override Road Clone()
        {
            return (ClassicalRoad)MemberwiseClone();
        }
    }

    /// <summary>
    /// 卡通风格的房屋
    /// </summary>
    public class CartoonHouse : House
    {
        public override House Clone()
        {
            return (CartoonHouse)MemberwiseClone();
        }
    }

    /// <summary>
    /// 卡通风格的道路
    /// </summary>
    public class CartoonRoad : Road
    {
        public override Road Clone()
        {
            return (CartoonRoad)MemberwiseClone();
        }
    }

    /// <summary>
    /// 另类风格的房屋
    /// </summary>
    public class AlternativeHouse : House
    {
        public override House Clone()
        {
            return (AlternativeHouse)MemberwiseClone();
        }
    }

    /// <summary>
    /// 另类风格的道路
    /// </summary>
    public class AlternativeRoad : Road
    {
        public override Road Clone()
        {
            return (AlternativeRoad)MemberwiseClone();
        }
    }
    #endregion

这时客户端的调用代码如下:

    class Program
    {
        static void Main(string[] args)
        {
            BuildSystem buildSystem = new BuildSystem();
            //构建卡通风格和另类风格的房屋和道路
            buildSystem.Builld(new CartoonHouse(), new AlternativeHouse(), new CartoonRoad(), new AlternativeRoad());
            //构建现代风格和古典风格的房屋和道路
            buildSystem.Builld(new ModernHouse(),new ClassicalHouse(),new ModernRoad(),new ClassicalRoad());
        }
    }

ok,重构后的代码,在抽象A相对稳定的情况,通过对实现细节b的抽象,让实现细节b和抽象A都依赖于抽象B,完成了依赖倒置,实现了代码new的解耦,这就是原型模式!

关于原型模式的几个要点:

1、Prototype模式用于隔离类对象的使用者和具体类型(易变类)的之间的耦合关系,但是这些易变类必须拥有稳定的接口.

2、Prototype模式对于"如何创建易变类的对象"采用"原型克隆"的方式来做,它使我们能非常灵活动态的创建某些拥有"稳定接口"的新对象.所需的工作仅仅是创建一个新类的对象即原型,然后在需要的地方不断的Clone.

3、Prototype模式的Clone方法可以利用Object自带的MemberwiseClone方法,注:该方法只能用于比较简单的类,只能实现浅拷贝,如果类中包含数组等引用类型,则需要使用序列化方法来实现类型的深拷贝

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏逍遥剑客的游戏开发

从Native到Web(三), NaCl学习笔记: 3D渲染(DX9迁移到GLES)

14320
来自专栏GreenLeaves

FactoryMethod工厂方法模式(创建型模式)

整个抽象的游戏设施建造系统相对变化较慢,本例中只有一个Build的创建方法,而Build内部的方法实现,该实现依赖与各种具体的实现,而这些实现变化的非常频繁,现...

8420
来自专栏java一日一条

Java高级软件工程师面试考纲

如果要应聘高级开发工程师职务,仅仅懂得Java的基础知识是远远不够的,还必须懂得常用数据结构、算法、网络、操作系统等知识。因此本文不会讲解具体的技术,笔者综合自...

8310
来自专栏Crossin的编程教室

【我问 Crossin】想转行做后端开发,要多久?

1 sys.argv[] 该如何使用? Crossin: sys.argv 是用来获取命令行参数的,sys.argv[0] 表示代码本身文件路径,所以参数从1开...

29550
来自专栏前端侠2.0

css3的transform造成z-index无效, 附我的牛逼解法

我想锁表头及锁定列。昨天新找的方法是用css3的transform,这个应该在IE9以上都可以的。

76030
来自专栏十月梦想

js三种引用方式

2.行内使用方式,在标签内声明一下,比如a标记herf内嵌套JavaScript:

24020
来自专栏AI科技大本营的专栏

一文总结学习Python的14张思维导图

本文主要涵盖了 Python 编程的核心知识(暂不包括标准库及第三方库,后续会发布相应专题的文章)。 首先,按顺序依次展示了以下内容的一系列思维导图:基础知识,...

382100
来自专栏熊二哥

.NET工作准备--01前言

01应聘须知(已过时) -1.了解软件开发大环境。 -2.准备简历:不宜超过一页,永远准备中文,模板。 -3.渠道:3大网站,中华英才,前程无忧(51job最...

22680
来自专栏数据结构与算法

2017 清北学堂 Day 6终极考试报告

预计分数: 100+70+70 = 240 实际假分数 : 40+80+70= 190  in cena(好吧不得不承认这个分数,,,,,,=.=) 实际真分数...

33430
来自专栏web前端教室

javascript ES6 初次相见

JS的ES6网上也热炒了好久了, 我一直也没怎么太细看, 今天想起来就写个东西, 也为分享,也为学习。 我喜欢接地气一点,所以网上的那些新名词我就不写了, 就写...

19970

扫码关注云+社区

领取腾讯云代金券