设计模式:以桥接模式和访问者模式为例,看设计模式在微信小游戏版本迭代中的应用(上)

简介: 去年9月份,微信小游戏《羊了个羊》火爆全网,由于同时在线玩家过多,开发商服务器2天之内竟然出现了3次宕机。这在云开发时代是极少出现的,若不是火爆程度大大超出了预期,程序员怎么可能来不及扩容服务器呢

image.png

去年9月份,微信小游戏《羊了个羊》火爆全网,由于同时在线玩家过多,开发商服务器2天之内竟然出现了3次宕机。这在云开发时代是极少出现的,若不是火爆程度大大超出了预期,程序员怎么可能来不及扩容服务器呢?


微信小游戏开发整体来讲简单、独立、易上手,即使是一个人,也可以开发,不少程序员还是独立的微信小游戏开发者,仅靠游戏收入就远远超过了一般程序员的上班收入。《羊了个羊》小游戏的火爆,更加刺激了程序员,尤其是前端程序员向这个领域转行。


为什么要在游戏开发中使用设计模式呢?


对于游戏开发,一般人认为这是一个创意行业,不仅要有过硬的技术,更要有新奇的创意。这个认知没有错,但是,创意是不受法律保护的,任何一个创意火爆以后,马上就可能有N个开发商跟风抄袭。在游戏行业的开发史上,已经出现过多次,第一个想出创意的老大被后来居上的老二反超了。


怎么应对这种情况呢?


如果别人跑得快,就要想办法比别人跑得更快,跑得更久。游戏开发和其他所有软件产品的开发一样,并不是一锤子买卖,在第一个版本上线以后,后续根据玩家反馈和竞品功能的升级,需要不断研发和推出新版本。


在版本迭代的过程中,怎么样让新功能更快地开发出来,同时老功能还能更大范围地保持稳定,这是最考验游戏架构师能力的。架构师在项目启动的时候,就要为后续可能的变化预留方案,让后面游戏版本的迭代进行得又快、又稳。这涉及游戏架构师的一项核心能力:渐进式模块化重构与面向对象重构的能力。


软件开发是有成熟的套路的,前辈大牛经过实践总结的设计模式便是套路的结晶,有意识地在游戏开发中运用成熟的设计模式,不仅可以彰显程序员的内功水平,还能在一定程度上保证版本迭代的快速与稳定。


当前的小游戏项目分析


接下来作者分享的,是来自《微信小游戏开发》这本书中的一个小游戏实战案例,项目进行到第11章,基本功能已经开发完了,为了方便读者锤炼渐进式模块化重构与面向对象重构的能力,特意在这个阶段安排了设计模式实战。


在目前的项目中(以《微信小游戏开发》前端篇随书源码第11章/11.1/11.1.1的源码为基础),有两类碰撞检测:一类发生在球与挡板之间;另一类发生在球与屏幕边界之间。在游戏中,碰撞检测是非常常见一种功能,为了应对可能增加的碰撞检测需求,我们使用设计模式将两类碰撞的耦合性降低,方便后续加入新的碰撞与被碰撞对象。


具体从实现上来讲,接下来我们准备应用桥接模式,将发生碰撞的双方,分别定义为两个可以独立变化的抽象对象(HitObjectRectangle与HitedObjectRectangle),然后再让它们的具体实现部分独立变化,以此完成对桥接模式的应用。


目前球(Ball)与挡板(Panel)还没有基类,我们可以让它们继承于新创建的抽象基类,但这样并不是很合理,它们都属于可视化对象,如果要继承,更应该继承于Component基类。在JS中一个类的继承只能实现单继续,不能让一个类同时继承于多个基类,在这种情况下我们怎么实现桥接模式中的抽象部分呢?对象能力的扩展形式,除了继承,还有复合,我们可以将定义好的桥接模式中的具体实现部分,以类属性的方式放在球和挡板对象中。


什么是桥接模式?


在应用桥接模式之前,我们先从概念上简单了解一下什么是桥接模式。

桥接模式是一种结构型设计模式, 可将一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。


换一个说法,桥接模式将对象的抽象部分与它的具体实现部分分离,使它们都可以独立的变化。在桥接模式中,一般包括两个抽象部分和两个具体实现的部分,一个抽象部分和一个具体实现部分为一组,一共有两组,两组通过中间的抽象部分进行桥接,从而让两组的具体实现部分可以相对独立自由的变化。


为了更好地理解这个模式,我们通过一张图看一个应用示例,如图11-1所示:

image.png

图11-1桥接模式示例示意图


在这张图中,中间是一个跨平台开发框架,它为开发者抽离出一套通用接口(抽象部分B),这些接口是通用的、系统无关的,借此开发框架实现了跨平台特性。在开发框架中,具体到每个系统(Mac、Windows和Linux),每个接口及UI有不同的实现(具体实现部分B1、B2、B3)。左边,在应用程序中,开发者在软件中定义了一套抽象部分A,在每个系统上有不同的具体实现(具体实现部分A1、A2、A3)。应用程序面向抽象部分B编程,不必关心开发框架在每个系统下的具体实现;应用程序的具体实现部分A1、A2、A3是基于抽象部分A编程的,它们也不需要知道抽象部分B。抽象部分A与抽象部分B之间仿佛有一个桥连接了起来,这两套抽象部分与其具体实现部分呈现的模式便是桥接模式。


试想一下,如果我们不使用桥接模式,没有中间这一层跨平台开发框架,没有抽象部分B和抽象部分A,这时候我们想实现具体实现部分A1、A2、A3,怎么做呢?直接在各个系统的基础类库上实现呢?让A1与B1耦合、A2与B2耦合、A3与B3耦合吗?每次在应用程序中添加一个新功能,都要在三个地方分别实现。而有了桥接模式之后,B1、B2、B3都不需要关心了,只需要知道抽象部分B就可以了;添加新功能时,只需要在抽象部分A中定义并基于抽象部分B实现核心功能就可以了,在具体实现部分A1、A2、A3中只是UI和交互方式不同而已。这是使用桥接模式的价值。


桥接模式的具体实现


接下来开始实践,我们先定义桥接模式当中的抽象部分,一个是主动撞击对象的抽象部分(HitObjectRectangle),一个是被动撞击对象的抽象部分(HitedObjectRectangle)。由于两个部分的抽象部分具有相似性,我们可以先定义一个抽象部分的基类Rectangle,如代码清单11-7所示:

代码清单11-7矩形基类


1.// JS:src\views\hitTest\rectangle.js
2./** 对象的矩形描述,默认将注册点放在左上角 */
3.class Rectangle {
4.  constructor(x, y, width, height) {
5.    this.x = x
6.    this.y = y
7.    this.width = width
8.    this.height = height
9.  }
10.
11.  /** X坐标 */
12.  x = 0
13.  /** Y坐标 */
14.  y = 0
15.  /** X轴方向上所占区域 */
16.  width = 0
17.  /** Y轴方向上所占区域 */
18.  height = 0
19.
20.  /** 顶部边界 */
21.  get top() {
22.    return this.y
23.  }
24.  /** 底部边界 */
25.  get bottom() {
26.    return this.y + this.height
27.  }
28.  /** 左边界 */
29.  get left() {
30.    return this.x
31.  }
32.  /** 右边界 */
33.  get right() {
34.    return this.x + this.width
35.  }
36.}
37.
38.export default Rectangle


上面代码做了什么事?


❑ 第12行至第18行,这是4个属性,x、y决定注册点,width、height决定尺寸。

❑ 第21行至第35行,这是4个getter访问器,分别代表对象在4个方向上的边界值。这4个属性不是实际存在的,而是通过注册点与尺寸计算出来的。根据注册点位置的不同,这4个getter的值也不同。默认注册点,即(0,0)坐标点在左上角,这时候top等于y;如果注册点在左下角,这时候top则等于y减去height。


Rectangle描述了一个对象的距形范围,关于4个边界属性top、bottom、left、right与注册点的关系,可以参见图11-2:


image.png


图11-2注册点与边界值的关系


接下来我们开始定义两个抽象部分:一个是撞击对象的,另一个是受撞击对象的。先看受撞击对象的,它比较简单:


1.// JS:src\views\hitTest\hited_object_rectangle.js
2.import Rectangle from "rectangle.js"
3.
4./** 被碰撞对象的抽象部分,屏幕及左右挡板的注册点默认在左上角 */
5.class HitedObjectRectangle extends Rectangle{
6.  constructor(x, y, width, height){
7.    super(x, y, width, height)
8.  }
9.}
10.
11.export default HitedObjectRectangle


HitedObjectRectangle类它没有新增属性或方法,所有特征都是从基类继承的。它的主要作用是被继承,稍后有3个子类继承它。


再看一下撞击对象的定义,如代码清单11-8所示:


代码清单11-8创建撞击对象基类


1.// JS:src\views\hitTest\hit_object_rectangle.js
2.import Rectangle from "rectangle.js"
3.import LeftPanelRectangle from "left_panel_rectangle.js"
4.import RightPanelRectangle from "right_panel_rectangle.js"
5.import ScreenRectangle from "screen_rectangle.js"
6.
7./** 碰撞对象的抽象部分,球与方块的注册点在中心,不在左上角 */
8.class HitObjectRectangle extends Rectangle {
9.  constructor(width, height) {
10.    super(GameGlobal.CANVAS_WIDTH / 2, GameGlobal.CANVAS_HEIGHT / 2, width, height)
11.  }
12.
13.  get top() {
14.    return this.y - this.height / 2
15.  }
16.  get bottom() {
17.    return this.y + this.height / 2
18.  }
19.  get left() {
20.    return this.x - this.width / 2
21.  }
22.  get right() {
23.    return this.x + this.width / 2
24.  }
25.
26.  /** 与被撞对象的碰撞检测 */
27.  hitTest(hitedObject) {
28.    let res = 0
29.    if (hitedObject instanceof LeftPanelRectangle) { // 碰撞到左挡板返回1
30.      if (this.left < hitedObject.right && this.top > hitedObject.top && this.bottom < hitedObject.bottom) {
31.        res = 1 << 0
32.      }
33.    } else if (hitedObject instanceof RightPanelRectangle) { // 碰撞到右挡板返回2
34.      if (this.right > hitedObject.left && this.top > hitedObject.top && this.bottom < hitedObject.bottom) {
35.        res = 1 << 1
36.      }
37.    } else if (hitedObject instanceof ScreenRectangle) {
38.      if (this.right > hitedObject.right) { // 触达右边界返回4
39.        res = 1 << 2
40.      } else if (this.left < hitedObject.left) { // 触达左边界返回8
41.        res = 1 << 3
42.      }
43.      if (this.top < hitedObject.top) { // 触达上边界返回16
44.        res = 1 << 4
45.      } else if (this.bottom > hitedObject.bottom) { // 触达下边界返回32
46.        res = 1 << 5
47.      }
48.    }
49.    return res
50.  }
51.}
52.
53.export default HitObjectRectangle

在上面代码中:


❑ HitObjectRectangle也是作为基类存在的,稍后有一个子类继承它。在这个基类中,第13行至第24行,我们通过重写getter访问器属性,将注册点由左上角移到了中心。


❑ 第10行,在构造器函数中我们看到,默认的起始x、y是屏幕中心的坐标。


❑ 第27行至第50行,hitTest方法的实现是核心代码,碰撞到左挡板与碰撞到右挡板返回的数字与之前定义的一样,碰撞四周墙壁返回的数字是4个新增的数字。


❑ 第35行,这行出现的1<<0代表数值的二进制向左移0个位置。移0个位置没有意义,这样书写是为了与下面的第35行、第39行、第41行等保持格式一致。1<<0等于1,1<<1等于2,1<<2等于4,1<<3等于8,这些数值是按2的N次幂递增的。


接下来我们定义ScreenRectangle,它是被撞击部分的具体实现部分:


1.// JS:src\views\hitTest\screen_rectangle.js
2.import HitedObjectRectangle from "hited_object_rectangle.js"
3.
4./** 被碰撞对象屏幕的大小数据 */
5.class ScreenRectangle extends HitedObjectRectangle {
6.  constructor() {
7.    super(0, 0, GameGlobal.CANVAS_WIDTH, GameGlobal.CANVAS_HEIGHT)
8.  }
9.}
10.
export default ScreenRectangle


ScreenRectangle是屏幕的大小、位置数据对象,是一个继承于HitedObjectRectangle的具体实现。ScreenRectangle类作为一个具体的实现类,却没有添加额外的属性或方法,那我们为什么要定义它呢?它存在的意义,是由它本身作为一个对象成立的,参见HitObjectRectangle类中的hitTest方法。

接下来我们再看左挡板的大小、位置数据对象:


1.// JS:src\views\hitTest\left_panel_rectangle.js
2.import HitedObjectRectangle from "hited_object_rectangle.js"
3.
4./** 被碰撞对象左挡板的大小数据 */
5.class LeftPanelRectangle extends HitedObjectRectangle {
6.  constructor() {
7.    super(0, (GameGlobal.CANVAS_HEIGHT - GameGlobal.PANEL_HEIGHT) / 2, GameGlobal.PANEL_WIDTH, GameGlobal.PANEL_HEIGHT)
8.  }
9.}
10.
11.export default LeftPanelRectangle


LeftPanelRectangle与ScreenRectangle一样,是继承于HitedObjectRectangle的一个具体实现,仍然没有新增属性或方法,所有信息,包括大小和位置,都已经通过构造器参数传递进去了。

再看一下右挡板的大小、位置数据对象:


1.// JS:src\views\hitTest\right_panel_rectangle.js
2.import HitedObjectRectangle from "hited_object_rectangle.js"
3.
4./** 被碰撞对象右挡板的大小数据 */
5.class RightPanelRectangle extends HitedObjectRectangle {
6.  constructor() {
7.    super(GameGlobal.CANVAS_WIDTH - GameGlobal.PANEL_WIDTH, (GameGlobal.CANVAS_HEIGHT - GameGlobal.PANEL_HEIGHT) / 2, GameGlobal.PANEL_WIDTH, GameGlobal.PANEL_HEIGHT)
8.  }
9.}
10.
11.export default RightPanelRectangle


RightPanelRectangle也是继承于HitedObjectRectangle的一个具体实现,与LeftPanelRectangle不同的只是坐标位置。


接下来我们再看撞击对象这边的具体实现部分,只有一个BallRectangle类:


1.// JS:src\views\hitTest\ball_rectangle.js
2.import HitObjectRectangle from "hit_object_rectangle.js"
3.
4./** 碰撞对象的具体实现部分,球的大小及运动数据对象 */
5.class BallRectangle extends HitObjectRectangle {
6.  constructor() {
7.    super(GameGlobal.RADIUS * 2, GameGlobal.RADIUS * 2)
8.  }
9.}
10.
11.export default BallRectangle


BallRectangle是描述球的位置、大小的,所有信息在基类中都具备了,所以它不需要添加任何属性或方法了。


以上就是我们为应用桥接模式定义的所有类了,为了进一步明确它们之间的关系,看一张示意图,如图11-3所示:


image.png

图11-3桥接模式示例类关系图


第二层的HitObjectRectangle和HitedObjectRectangle是桥接模式中的抽象部分,第三层是具体实现部分。事实上如果我们需要的话,我们在HitObjectRectangle和HitedObjectRectangle两条支线上,还可以定义更多的具体实现类。


在项目中消费桥接模式


接下来看如何使用,先改造原来的Ball类,如代码清单11-9所示:

代码清单11-9改造Ball类


1.// JS:src/views/ball.js
2.import BallRectangle from "hitTest/ball_rectangle.js"
3.
4./** 小球 */
5.class Ball {
6.  ...
7.
8.  constructor() { }
9.
10.  get x() {
11.    // return this.#pos.x
12.    return this.rectangle.x
13.  }
14.  get y() {
15.    // return this.#pos.y
16.    return this.rectangle.y
17.  }
18.  /** 小于碰撞检测对象 */
19.  rectangle = new BallRectangle()
20.  // #pos // 球的起始位置
21.  #speedX = 4 // X方向分速度
22.  #speedY = 2 // Y方向分速度
23.
24.  /** 初始化 */
25.  init(options) {
26.    // this.#pos = options?.ballPos ?? { x: GameGlobal.CANVAS_WIDTH / 2, y: GameGlobal.CANVAS_HEIGHT / 2 } 
27.    // const defaultPos = { x: this.#pos.x, y: this.#pos.y }
28.    // this.reset = () => {
29.    //   this.#pos.x = defaultPos.x
30.    //   this.#pos.y = defaultPos.y
31.    // }
32.    this.rectangle.x = options?.x ?? GameGlobal.CANVAS_WIDTH / 2
33.    this.rectangle.y = options?.y ?? GameGlobal.CANVAS_HEIGHT / 2
34.    this.#speedX = options?.speedX ?? 4
35.    this.#speedY = options?.speedY ?? 2
36.    const defaultArgs = Object.assign({}, this.rectangle)
37.    this.reset = () => {
38.      this.rectangle.x = defaultArgs.x
39.      this.rectangle.y = defaultArgs.y
40.      this.#speedX = 4
41.      this.#speedY = 2
42.    }
43.  }
44.
45.  /** 重设 */
46.  reset() { }
47.
48.  /** 渲染 */
49.  render(context) {
50.    ...
51.  }
52.
53.  /** 运行 */
54.  run() {
55.    // 小球运动数据计算
56.    // this.#pos.x += this.#speedX
57.    // this.#pos.y += this.#speedY
58.    this.rectangle.x += this.#speedX
59.    this.rectangle.y += this.#speedY
60.  }
61.
62.  /** 小球与墙壁的四周碰撞检查 */
63.  // testHitWall() {
64.  //   if (this.#pos.x > GameGlobal.CANVAS_WIDTH - GameGlobal.RADIUS) { // 触达右边界
65.  //     this.#speedX = -this.#speedX
66.  //   } else if (this.#pos.x < GameGlobal.RADIUS) { // 触达左边界
67.  //     this.#speedX = -this.#speedX
68.  //   }
69.  //   if (this.#pos.y > GameGlobal.CANVAS_HEIGHT - GameGlobal.RADIUS) { // 触达右边界
70.  //     this.#speedY = -this.#speedY
71.  //   } else if (this.#pos.y < GameGlobal.RADIUS) { // 触达左边界
72.  //     this.#speedY = -this.#speedY
73.  //   }
74.  // }
75.  testHitWall(hitedObject) {
76.    const res = this.rectangle.hitTest(hitedObject)
77.    if (res === 4 || res === 8) {
78.      this.#speedX = -this.#speedX
79.    } else if (res === 16 || res === 32) {
80.      this.#speedY = -this.#speedY
81.    }
82.  }
83.
84.  ...
85.}
86.
87.export default Ball.getInstance()


在Ball类中发生了什么变化?


❑ 第19行,我们添加了新的类属性rectangle,它是BallRectangle的实例。所有关于球的位置、大小等信息都移到了rectangle中,所以原来的类属性#pos(第20行)不再需要了,同时原来调用它的代码(例如第58行、第59行)都需要使用rectangle改写。

❑ 第32行至第42行,这是初始化代码,原来#pos是一个坐标,包括x、y两个值,现在将这两个值分别以rectangle中的x、y代替。

❑ 方法testHitWall用于屏幕边缘碰撞检测的,第63行至第74行的是旧代码,第75行至第82行是新代码。hitedObject是新增的参数,它是HitedObjectRectangle子类的实例。

小球属于撞击对象,它的rectangle是一个HitObjectRectangle的子类实例(BallRectangle)。

看一下对Panel类的改造,它是LeftPanel和RightPanel的基类,如代码清单11-10所示:

代码清单11-10改造Panel类


1.// JS:src/views/panel.js
2./** 挡板基类 */
3.class Panel {
4.  constructor() { }
5.
6.  // x // 挡板的起点X坐标
7.  // y // 挡板的起点Y坐标
8.  get x() {
9.    return this.rectangle.x
10.  }
11.  set x(val) {
12.    this.rectangle.x = val
13.  }
14.  get y() {
15.    return this.rectangle.y
16.  }
17.  set y(val) {
18.    this.rectangle.y = val
19.  }
20.  /** 挡板碰撞检测对象 */
21.  rectangle
22.  ...
23.}
24.
25.export default Panel


这个基类发生了什么变化?


❑ 第21行,rectangle是新增的HitedObjectRectangle的子类实例,具体是哪个实现,要在子类中决定。

❑ 第6行、第7行将x、y去掉,代之以第8行至第19行的getter访问器和setter设置器,对x、y属性的访问和设置,将转变为对rectangle中x、y的访问和设置。

为什么要在Panel基类中新增一个rectangle属性?因为要在它的子类LeftPanel、RightPanel中新增这个属性,挡板是被撞击对象,rectangle是HitedObjectRectangle的子类实例。与其在子类中分别设置,不如在基类中一个地方统一设置;另外,基类中render方法渲染挡板时要使用x、y属性,x、y属性需要重写,这也要求rectangle必须定义在基类中定义。

看一下对LeftPanel类的改造,如代码清单11-11所示:

代码清单11-11改造LeftPanel类



1.// JS:src/views/left_panel.js
2....
3.import LeftPanelRectangle from "hitTest/left_panel_rectangle.js"
4.
5./** 左挡板 */
6.class LeftPanel extends Panel {
7.  constructor() {
8.    super()
9.    this.rectangle = new LeftPanelRectangle()
10.  }
11.
12.  ...
13.
14.  /** 小球碰撞到左挡板返回1 */
15.  testHitBall(ball) {
16.    return ball.rectangle.hitTest(this.rectangle)
17.    // if (ball.x < GameGlobal.RADIUS + GameGlobal.PANEL_WIDTH) { // 触达左挡板
18.    //   if (ball.y > this.y && ball.y < (this.y + GameGlobal.PANEL_HEIGHT)) {
19.    //     return 1
20.    //   }
21.    // }
22.    // return 0
23.  }
24.}
25.
26.export default new LeftPanel()


上面发生了什么?只有两处改动:


❑ 第9行,这里决定了基类中的rectangle是LeftPanelRectangle实例。LeftPanelRectangle是HitedObjectRectangle的子类。

❑ 第16行,碰撞检测代码修改为:由小球的rectangle与当前对象的rectangle做碰撞测试。

接下来是对RightPanel类的改写,如代码清单11-12所示:


代码清单11-12改造RightPanel类


1.// JS:src/views/right_panel.js
2....
3.import RightPanelRectangle from "hitTest/right_panel_rectangle.js"
4.
5./** 右挡板 */
6.class RightPanel extends Panel {
7.  constructor() {
8.    super()
9.    this.rectangle = new RightPanelRectangle()
10.  }
11.
12.  ...
13.
14.  /** 小球碰撞到左挡板返回2 */
15.  testHitBall(ball) {
16.    return ball.rectangle.hitTest(this.rectangle)
17.    // if (ball.x > (GameGlobal.CANVAS_WIDTH - GameGlobal.RADIUS - GameGlobal.PANEL_WIDTH)) { // 碰撞右挡板
18.    //   if (ball.y > this.y && ball.y < (this.y + GameGlobal.PANEL_HEIGHT)) {
19.    //     return 2
20.    //   }
21.    // }
22.    // return 0
23.  }
24.}
25.
26.export default new RightPanel()


与LeftPanel类似,在这个RightPanel类中也只有两处修改,见第9行与第16行。

最后,我们开始改造GameIndexPage,它是我们应用桥接模式的最后一站了,如代码清单11-13所示:


代码清单11-13改造游戏主页对象


1.// JS:src\views\game_index_page.js
2....
3.import ScreenRectangle from "hitTest/screen_rectangle.js"
4.
5./** 游戏主页页面 */
6.class GameIndexPage extends Page {
7.  ...
8.  /** 墙壁碰撞检测对象 */
9.  #rectangle = new ScreenRectangle()
10.
11.  ...
12.
13.  /** 运行 */
14.  run() {
15.    ...
16.    // 小球碰撞检测
17.    // ball.testHitWall()
18.    ball.testHitWall(this.#rectangle)
19.    ...
20.  }
21.
22.  ...
23.}
24.
export default GameIndexPage


在GameIndexPage类中,只有两处修改:


❑ 第9行,添加了一个私有属性#rectangle,它是一个碰撞检测数据对象,是HitedObjectRectangle的子类实例。

❑ 第18行,在调用小球的testHitWall方法,将#rectangle作为参数传递了进去。


现在代码修改完了,重新编译测试,运行效果与之前一致,如下所示。


image.png

目录
相关文章
|
7天前
|
设计模式 开发者 Python
Python编程中的设计模式应用与实践感悟####
本文作为一篇技术性文章,旨在深入探讨Python编程中设计模式的应用价值与实践心得。在快速迭代的软件开发领域,设计模式如同导航灯塔,指引开发者构建高效、可维护的软件架构。本文将通过具体案例,展现设计模式如何在实际项目中解决复杂问题,提升代码质量,并分享个人在实践过程中的体会与感悟。 ####
|
1月前
|
存储 缓存 开发框架
提高微信小程序的应用速度
【10月更文挑战第21天】提高微信小程序的应用速度需要从多个方面入手,综合运用各种优化手段。通过不断地优化和改进,能够显著提升小程序的性能,为用户带来更流畅、更高效的使用体验。
50 3
|
1月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
19 1
|
2月前
|
设计模式 PHP 开发者
PHP中的设计模式:桥接模式的解析与应用
在软件开发的浩瀚海洋中,设计模式如同灯塔一般,为开发者们指引方向。本文将深入探讨PHP中的一种重要设计模式——桥接模式。桥接模式巧妙地将抽象与实现分离,通过封装一个抽象的接口,使得实现和抽象可以独立变化。本文将阐述桥接模式的定义、结构、优缺点及其应用场景,并通过具体的PHP示例代码展示如何在实际项目中灵活运用这一设计模式。让我们一起走进桥接模式的世界,感受它的魅力所在。
|
2月前
|
设计模式 测试技术 持续交付
架构视角下的NHibernate:设计模式与企业级应用考量
【10月更文挑战第13天】随着软件开发向更复杂、更大规模的应用转变,数据访问层的设计变得尤为重要。NHibernate作为一个成熟的对象关系映射(ORM)框架,为企业级.NET应用程序提供了强大的支持。本文旨在为有一定经验的开发者提供一个全面的指南,介绍如何在架构层面有效地使用NHibernate,并结合领域驱动设计(DDD)原则来构建既强大又易于维护的数据层。
39 2
|
2月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入解析与应用
【10月更文挑战第8天】 在软件开发的浩瀚宇宙中,设计模式如同星辰指引,照亮了代码设计与架构的航道。本文旨在深入探索PHP语境下策略模式(Strategy Pattern)的精髓,不仅剖析其内核原理,还将其融入实战演练,让理论在实践中生根发芽。策略模式,作为解决“如何优雅地封装算法族”的答案,以其独特的灵活性与扩展性,赋予PHP应用以动态变换行为的能力,而无需牵动既有的类结构。
30 2
|
2月前
|
设计模式 开发者 Python
Python编程中的设计模式应用与实践###
【10月更文挑战第18天】 本文深入探讨了Python编程中设计模式的应用与实践,通过简洁明了的语言和生动的实例,揭示了设计模式在提升代码可维护性、可扩展性和重用性方面的关键作用。文章首先概述了设计模式的基本概念和重要性,随后详细解析了几种常用的设计模式,如单例模式、工厂模式、观察者模式等,在Python中的具体实现方式,并通过对比分析,展示了设计模式如何优化代码结构,增强系统的灵活性和健壮性。此外,文章还提供了实用的建议和最佳实践,帮助读者在实际项目中有效运用设计模式。 ###
20 0
|
29天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###