常用的设计模式有哪几种(常用设计模式之结构性设计模式)

引言

大家好,我是一个努力护发不脱发的萌新程序员,上次跟大家分享了创建型设计模式,这次就来分享结构性设计模式。结构性设计模式和创建型设计模式一样都需要遵循相同的设计原则,下方有上次分享的设计原则和创建者模式的链接,感兴趣的朋友可以回顾一下。

结构性设计模式

1、装饰者模式

  • 动机(Motivate)

在房子装修的过程中,各种功能可以相互组合,来增加房子的功用。类似的,如果我们在软件系统中,要给某个类型或者对象增加功能,如果使用“继承”的方案来写代码,就会出现子类暴涨的情况。比如:IMarbleStyle是大理石风格的一个功能,IKeepWarm是保温的一个接口定义,IHouseSecurity是房子安全的一个接口,就三个接口来说,House是我们房子,我们的房子要什么功能就实现什么接口,如果房子要的是复合功能,接口不同的组合就有不同的结果,这样就导致我们子类膨胀严重,如果需要在增加功能,子类会成指数增长。这个问题的根源在于我们“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质(所谓静态特质,就是说如果想要某种功能,我们必须在编译的时候就要定义这个类,这也是强类型语言的特点。静态,就是指在编译的时候要确定的东西;动态,是指运行时确定的东西),使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。如何使“对象功能的扩展”能够根据需要来动态(即运行时)地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?

  • 意图(Intent)

动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活。 —— 《设计模式》GoF

  • 结构图(Structure)

常用的设计模式有哪几种(常用设计模式之结构性设计模式)(1)

  • 模式的组成

在装饰模式中的各个角色有:

(1)、抽象构件角色(Component):给出一个抽象接口,以规范准备接收附加责任的对象。

(2)、具体构件角色(Concrete Component):定义一个将要接收附加责任的类。

(3)、装饰角色(Decorator):持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。

(4)、具体装饰角色(Concrete Decorator):负责给构件对象添加上附加的责任。

代码实现

namespace DecoratorPattern { /// <summary> /// 首先我们先定义一个基础的抽象类,让裸妆类和物品类来实现它 /// </summary> public abstract class BaseCharactor { public abstract void Show(); } /// <summary> /// 一个裸妆的法师,什么装备都没有的,将要通过装饰者模式来拥有一切,哈哈哈哈。 /// </summary> class Mage : BaseCharactor { public override void Show() { Console.WriteLine("我是一个裸装法师Jobs,下面是我获得的装备!"); } } /// <summary> /// 通过传参,调用传参的方法,再在基础上加入获得帽子。 /// </summary> public class BaseDecorator : BaseCharactor { private BaseCharactor _Basecharactor = null; public BaseDecorator(BaseCharactor charactor) { this._Basecharactor = charactor; } public override void Show() { this._Basecharactor.Show(); Console.WriteLine("我获得了一个帽子"); } } /// <summary> /// 这是一个能让法师获得好帽子的类,同样把传参也就是要被装饰的类再套上一层, /// 如果再之前传参已经套上了很多层,也会之前的方法都实现。 /// </summary> public class BaseHelmet : BaseDecorator { private BaseCharactor _Basecharactor = null; public BaseHelmet(BaseCharactor charactor):base(charactor) { this._Basecharactor = charactor; } public override void Show() { _Basecharactor.Show(); Console.WriteLine("我获得了一个好帽子!"); } } /// <summary> /// 这是一个获得胸甲类,同样把传参也就是要被装饰的类再套上一层。 /// </summary> public class BaseChest : BaseDecorator { private BaseCharactor _Basecharactor = null; public BaseChest(BaseCharactor charactor):base(charactor) { this._Basecharactor = charactor; } public override void Show() { _Basecharactor.Show(); Console.WriteLine("我获得了一个胸甲"); } } /// <summary> /// 这是一个能让法师获得肩甲的类,同样把传参也就是要被装饰的类再套上一层, /// 如果再之前传参已经套上了很多层,也会之前的方法都实现。 /// </summary> public class BaseShoulder : BaseDecorator { private BaseCharactor _Basecharactor = null; public BaseShoulder(BaseCharactor charactor):base(charactor) { this._Basecharactor = charactor; } public override void Show() { _Basecharactor.Show(); Console.WriteLine("我获得了一个肩甲"); } } /// <summary> /// 实现类,想让裸妆的法师获得什么,就可以调整它得顺序。 /// 也就是把它一层一层得套。 /// </summary> class Program { static void Main(string[] args) { Console.WriteLine("----------------------"); BaseCharactor charactor = new Mage(); charactor = new BaseDecorator(charactor); charactor = new BaseHelmet(charactor); charactor = new BaseShoulder(charactor); charactor = new BaseChest(charactor); charactor.Show(); Console.Read(); } } }

2、享元模式

  • 动机(Motivate)

在软件系统中,采用纯粹对象方案的问题在于大量细粒度的对象会很快充斥在系统中,从而带来很高的运行时代价——主要指内存需求方面的代价。如何在避免大量细粒度对象问题的同时,让外部客户程序仍然能够透明地使用面向对象的方式来进行操作?

  • 意图(Intent)

运用共享技术有效地支持大量细粒度的对象。 ——《设计模式》GoF

  • 结构图(Structure)

i

常用的设计模式有哪几种(常用设计模式之结构性设计模式)(2)

  • 模式的组成

(1)、抽象享元角色(Flyweight):此角色是所有的具体享元类的基类,为这些类规定出需要实现的公共接口。那些需要外部状态的操作可以通过调用方法以参数形式传入。

(2)、具体享元角色(ConcreteFlyweight):实现抽象享元角色所规定的接口。如果有内部状态的话,可以在类内部定义。

(3)、享元工厂角色(Flyweightfactory):本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享,当一个客户端对象调用一个享元对象的时候,享元工厂角色检查系统中是否已经有一个符合要求的享元对象,如果已经存在,享元工厂角色就提供已存在的享元对象,如果系统中没有一个符合的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。

(4)、客户端角色(Client):本角色需要存储所有享元对象的外部状态。

代码实现

namespace 享元模式的实现 { /// <summary> /// 享元模式不是很难,但是有些状态需要单独处理,以下就是该模式的C#实现。 /// </summary> class Client { static void Main(string[] args) { //比如,我们现在需要10000个一般士兵,只需这样 SoldierFactory factory = new SoldierFactory(); AK47 ak47 = new AK47(); for (int i = 0; i < 100; i ) { Soldier soldier = null; if (i <= 20) { soldier = factory.GetSoldier("士兵" (i 1), ak47, SoldierType.Normal); } else { soldier = factory.GetSoldier("士兵" (i 1), ak47, SoldierType.Water); } soldier.Fight(); } //我们有这么多的士兵,但是使用的内存不是很多,因为我们缓存了。 Console.Read(); } } //这些是辅助类型 public enum SoldierType { Normal, Water } //该类型就是抽象战士Soldier--该类型相当于抽象享元角色 public abstract class Soldier { //通过构造函数初始化士兵的名称 protected Soldier(string name) { this.Name = name; } //士兵的名字 public string Name { get; private set; } //可以传入不同的武器就用不同的活力---该方法相当于抽象Flyweight的Operation方法 public abstract void Fight(); public Weapen WeapenInstance { get; set; } } //一般类型的战士,武器就是步枪---相当于具体的Flyweight角色 public sealed class NormalSoldier : Soldier { //通过构造函数初始化士兵的名称 public NormalSoldier(string name) : base(name) { } //执行享元的方法---就是Flyweight类型的Operation方法 public override void Fight() { WeapenInstance.Fire("士兵:" Name " 在陆地执行击毙任务"); } } //这是海军陆战队队员,武器精良----相当于具体的Flyweight角色 public sealed class WaterSoldier : Soldier { //通过构造函数初始化士兵的名称 public WaterSoldier(string name) : base(name) { } //执行享元的方法---就是Flyweight类型的Operation方法 public override void Fight() { WeapenInstance.Fire("士兵:" Name " 在海中执行击毙任务"); } } //此类型和享元没太大关系,可以算是享元对象的状态吧,需要从外部定义 public abstract class Weapen { public abstract void Fire(string jobName); } //此类型和享元没太大关系,可以算是享元对象的状态吧,需要从外部定义 public sealed class AK47:Weapen { public override void Fire(string jobName) { Console.WriteLine(jobName); } } //该类型相当于是享元的工厂---相当于FlyweightFactory类型 public sealed class SoldierFactory { private static IList<Soldier> soldiers; static SoldierFactory() { soldiers = new List<Soldier>(); } Soldier mySoldier = null; //因为我这里有两种士兵,所以在这里可以增加另外一个参数,士兵类型,原模式里面没有, public Soldier GetSoldier(string name, Weapen weapen, SoldierType soldierType) { foreach (Soldier soldier in soldiers) { if (string.Compare(soldier.Name, name, true) == 0) { mySoldier = soldier; return mySoldier; } } //我们这里就任务名称是唯一的 if (soldierType == SoldierType.Normal) { mySoldier = new NormalSoldier(name); } else { mySoldier = new WaterSoldier(name); } mySoldier.WeapenInstance = weapen; soldiers.Add(mySoldier); return mySoldier; } } }

3、代理模式

  • 动机(Motivate)

在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者、或者系统结构带来很多麻烦。如何在不失去透明操作对象的同时来管理/控制这些对象特有的复杂性?增加一层间接层是软件开发中常见的解决方式。

  • 意图(Intent)

为其他对象提供一种代理以控制对这个对象的访问。 ——《设计模式》GoF

  • 结构图(Structure)

常用的设计模式有哪几种(常用设计模式之结构性设计模式)(3)

  • 模式的组成

代理模式所涉及的角色有三个:

(1)、抽象主题角色(Subject):声明了真实主题和代理主题的公共接口,这样一来在使用真实主题的任何地方都可以使用代理主题。

(2)、代理主题角色(Proxy):代理主题角色内部含有对真实主题的引用,从而可以操作真实主题对象;代理主题角色负责在需要的时候创建真实主题对象;代理角色通常在将客户端调用传递到真实主题之前或之后,都要执行一些其他的操作,而不是单纯地将调用传递给真实主题对象。

(3)、真实主题角色(RealSubject):定义了代理角色所代表的真实对象。

代码实现

namespace 代理模式的实现 { /// <summary> /// 大明星都有钱,有钱了,就可以请自己的经纪人了,有了经纪人,很多事情就不用自己亲力亲为。弄点绯闻,炒作一下子通过经纪人就可以名正言顺的的操作了,万一搞不好,自己也可以否认。 /// </summary> class Client { static void Main(string[] args) { //近期,Fan姓明星关注度有点下降,来点炒作 AgentAbstract fan = new AgentPerson(); fan.Speculation("偶尔出来现现身,为炒作造势"); Console.WriteLine(); //过了段时间,又不行了,再炒作一次 fan.Speculation("这段时间不火了,开始离婚炒作"); Console.Read(); } } //该类型就是抽象Subject角色,定义代理角色和真实主体角色共有的接口方法 public abstract class AgentAbstract { //该方法执行具体的炒作---该方法相当于抽象Subject的Request方法 public virtual void Speculation(string thing) { Console.WriteLine(thing); } } //该类型是Fan姓明星,有钱有势,想炒什么炒什么---相当于具体的RealSubject角色 public sealed class FanStar : AgentAbstract { //有钱有势,有背景啊 public FanStar() { } //要有名气,定期要炒作---就是RealSubject类型的Request方法 public override void Speculation(string thing) { Console.WriteLine(thing); } } //该类型是代理类型----相当于具体的Proxy角色 public sealed class AgentPerson : AgentAbstract { //这是背后的老板, private FanStar boss; //老板在后面发号施令 public AgentPerson() { boss = new FanStar(); } //炒作的方法,执行具体的炒作---就是Proxy类型的Request方法 public override void Speculation(string thing) { Console.WriteLine("前期弄点绯闻,拍点野照"); base.Speculation(thing); Console.WriteLine("然后开发布会,伤心哭泣,继续捞钱"); } } }

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页