最近学习设计模式和TypeScript,发现网上的资料略显硬核,不太容易理解记忆,经常看完就忘。作为一名游戏玩家,发现游戏中的很多场景都能和相应的设计模式相关联,不仅便于理解,更利于合理地运用设计模式。由于个人水平有限,只整理个人觉得比较有趣的设计模式,每个模式采用**哲学三问**进行讲解。如果对你有帮助的话,欢迎点赞和收藏,图片源自网络,侵删。
目录 设计模式单例模式
What
Game:在DNF一起组团刷本的日子中,副本BOSS看作为一个单例,玩家可以通过各种技能或者平A去消耗BOSS。
并且该本中的所有玩家都是对同一个BOSS造成伤害
How
TS版本
class Boss{
private static instance: Boss = null;
private hp: number = 1000;
getInjured(harm: number){
this.hp -= harm;
}
getHp(): number{
return this.hp;
}
static getInstance(): Boss{
// 如果已经实例化了那么直接返回
if(!this.instance){
this.instance = new Boss();
}
return this.instance;
}
}
class Player{
constructor(private name: string){}
attack(harm: number,boss: Boss): void{
boss.getInjured(harm);
console.log(`我是一名${this.name},打出了${harm}伤害,BOSS还剩${boss.getHp()}血`)
}
}
let p1: Player = new Player('鬼泣');
let p2: Player = new Player('街霸');
let p3: Player = new Player('阿修罗');
// 对同一个boss疯狂输出
p1.attack(100,Boss.getInstance());
p2.attack(80,Boss.getInstance());
p3.attack(120,Boss.getInstance());
// 我是一名鬼泣,打出了100伤害,BOSS还剩900血
// 我是一名街霸,打出了80伤害,BOSS还剩820血
// 我是一名阿修罗,打出了120伤害,BOSS还剩700血
JS版本
var Boss = /** @class */ (function () {
function Boss() {
this.hp = 1000;
}
Boss.prototype.getInjured = function (harm) {
this.hp -= harm;
};
Boss.prototype.getHp = function () {
return this.hp;
};
Boss.getInstance = function () {
// 如果已经实例化了那么直接返回
if (!this.instance) {
this.instance = new Boss();
}
return this.instance;
};
Boss.instance = null;
return Boss;
}());
var Player = /** @class */ (function () {
function Player(name) {
this.name = name;
}
Player.prototype.attack = function (harm, boss) {
boss.getInjured(harm);
console.log("我是一名" this.name ",打出了" harm "伤害,BOSS还剩" boss.getHp() "血");
};
return Player;
}());
var p1 = new Player('鬼泣');
var p2 = new Player('街霸');
var p3 = new Player('阿修罗');
// 对同一个boss疯狂输出
p1.attack(100, Boss.getInstance());
p2.attack(80, Boss.getInstance());
p3.attack(120, Boss.getInstance());
// 我是一名鬼泣,打出了100伤害,BOSS还剩900血
// 我是一名街霸,打出了80伤害,BOSS还剩820血
// 我是一名阿修罗,打出了120伤害,BOSS还剩700血
Why
优点
缺点
应用场景:针对一些静态的共享资源,可以采用单例模式对其进行访问。从此处可以看出举的游戏例子本身不太适合用单例模式(因为它是一个频繁变动的对象)
策略模式
What
Game:在炉石传说游戏中,盗贼的两套经典卡牌奇迹贼和爆牌贼,虽然都是盗贼玩家取胜的法宝,但这两套牌的取胜之道却截然不同。奇迹贼依靠一回合的极限操作往往能转危为安,让对面突然去世;而爆牌贼则是以疲劳和爆对面key牌的运营方式取胜。
How
TS版本
interface Deck{
name: string;
kill(): void;
}
class MiracleDeck implements Deck{
name: string = '奇迹贼';
kill(){
console.log(`${this.name}:十七张牌就是能秒你`);
}
}
class ExplosiveDeck implements Deck{
name: string = '爆牌贼';
kill(){
console.log(`${this.name}:我要爆光你的牌库!`)
}
}
class Robber{
private deck: Deck;
setDeck(deck: Deck){
this.deck = deck;
}
winTheGame(): void{
this.deck.kill();
};
}
let rb = new Robber();
rb.setDeck(new MiracleDeck());
rb.winTheGame();
rb.setDeck(new ExplosiveDeck());
rb.winTheGame();
// 奇迹贼:十七张牌就是能秒你
// 爆牌贼:我要爆光你的牌库!
JS版本
var MiracleDeck = /** @class */ (function () {
function MiracleDeck() {
this.name = '奇迹贼';
}
MiracleDeck.prototype.kill = function () {
console.log(this.name "十七张牌就是能秒你");
};
return MiracleDeck;
}());
var ExplosiveDeck = /** @class */ (function () {
function ExplosiveDeck() {
this.name = '爆牌贼';
}
ExplosiveDeck.prototype.kill = function () {
console.log(this.name "我要爆光你的牌库!");
};
return ExplosiveDeck;
}());
var Robber = /** @class */ (function () {
function Robber() {
}
Robber.prototype.setDeck = function (deck) {
this.deck = deck;
};
Robber.prototype.winTheGame = function () {
this.deck.kill();
};
;
return Robber;
}());
var rb = new Robber();
rb.setDeck(new MiracleDeck());
rb.winTheGame();
rb.setDeck(new ExplosiveDeck());
rb.winTheGame();
// 奇迹贼:十七张牌就是能秒你
// 爆牌贼:我要爆光你的牌库!
Why
优点
算法可以自由切换,可扩展性好
缺点
策略类会增多并且每个策略类都需要向外暴露
应用场景:多态场景,如分享功能,分享到不同平台的内部实现是不相同的
代理模式
What
定义:为一个对象提供一个代理者,以便控制对它的访问
解释:比如明星和经纪人的关系,经纪人会帮助明星处理商演赞助等细节问题,明星负责签字就好
Game:作为一个`FM`懒人玩家,只想把时间花在看模拟比赛上,其他不想做的事就很开心地甩给助理教练啦
How
TS版本
interface Match{
play(): void;
}
class Manager implements Match{
play(): void{
console.log('比赛开始了,是由皇家马德里对阵拜仁慕尼黑。。。')
}
}
class Cotch implements Match{
private manager: Manager = new Manager();
beforePlay(): void{
console.log('布置战术');
console.log('球队训话');
}
afterPlay(): void{
console.log('赛后采访');
}
play(): void{
this.beforePlay();
this.manager.play();
this.afterPlay();
}
}
let proxy: Cotch = new Cotch();
proxy.play();
// 布置战术
// 球队训话
// 比赛开始了,是由皇家马德里对阵拜仁慕尼黑。。。
// 赛后采访
JS版本
var Manager = /** @class */ (function () {
function Manager() {
}
Manager.prototype.play = function () {
console.log('比赛开始了,是由皇家马德里对阵拜仁慕尼黑。。。');
};
return Manager;
}());
var Cotch = /** @class */ (function () {
function Cotch() {
this.manager = new Manager();
}
Cotch.prototype.beforePlay = function () {
console.log('布置战术');
console.log('球队训话');
};
Cotch.prototype.afterPlay = function () {
console.log('赛后采访');
};
Cotch.prototype.play = function () {
this.beforePlay();
this.manager.play();
this.afterPlay();
};
return Cotch;
}());
var proxy = new Cotch();
proxy.play();
// 布置战术
// 球队训话
// 比赛开始了,是由皇家马德里对阵拜仁慕尼黑。。。
// 赛后采访
Why
优点
缺点
发布-订阅模式
What
定义:对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知
解释:比如JS中的addEventListener
Game:进游戏后你会订阅队友和敌人的生存状态,当生存状态发生变化时,系统会及时通知你
How
TS版本
class EventEmitter{
private events: Object = {}; // 存储事件
private key: number = 0; // 事件的唯一标识key
on(name: string,event: any): number{
event.key = this.key;
this.events[name] ? this.events[name].push(event)
: (this.events[name] = []) && this.events[name].push(event);
return this.key;
}
off(name: string,key: number){
if(this.events[name]){
this.events[name] = this.events[name].filter(x => x.key !== key);
}else{
this.events[name] = [];
}
}
emit(name: string,key?: number){
if(this.events[name].length === 0 ) throw Error(`抱歉,你没有定义 ${name}监听器`)
if(key){
this.events[name].forEach(x => x.key === key && x());
}else {
this.events[name].forEach(x => x());
}
}
}
let player: EventEmitter = new EventEmitter();
player.on('friendDied',function(): void{
console.log('你可爱的队友已被击*');
})
player.on('enemyDied',function(): void{
console.log('打的好呀,小帅哥')
})
// 模拟战况
let rand: number;
let k: number = 1;
while(k < 10){
rand = Math.floor(Math.random() * 10);
if(rand % 2 === 0){
player.emit('friendDied');
}else{
player.emit('enemyDied');
}
k ;
}
// 你可爱的队友已被击*
// 打的好呀,小帅哥
// 你可爱的队友已被击*
// 你可爱的队友已被击*
// 打的好呀,小帅哥
// 你可爱的队友已被击*
// 你可爱的队友已被击*
// 打的好呀,小帅哥
// 打的好呀,小帅哥
JS版本
var EventEmitter = /** @class */ (function () {
function EventEmitter() {
this.events = {}; // 存储事件
this.key = 0; // 事件的唯一标识key
}
EventEmitter.prototype.on = function (name, event) {
event.key = this.key;
this.events[name] ? this.events[name].push(event)
: (this.events[name] = []) && this.events[name].push(event);
return this.key;
};
EventEmitter.prototype.off = function (name, key) {
if (this.events[name]) {
this.events[name] = this.events[name].filter(function (x) { return x.key !== key; });
}
else {
this.events[name] = [];
}
};
EventEmitter.prototype.emit = function (name, key) {
if (this.events[name].length === 0)
throw Error("\u62B1\u6B49\uFF0C\u4F60\u6CA1\u6709\u5B9A\u4E49 " name "\u76D1\u542C\u5668");
if (key) {
this.events[name].forEach(function (x) { return x.key === key && x(); });
}
else {
this.events[name].forEach(function (x) { return x(); });
}
};
return EventEmitter;
}());
var player = new EventEmitter();
player.on('friendDied', function () {
console.log('你可爱的队友已被击*');
});
player.on('enemyDied', function () {
console.log('打的好呀,小帅哥');
});
// 模拟战况
var rand;
var k = 1;
while (k < 10) {
rand = Math.floor(Math.random() * 10);
if (rand % 2 === 0) {
player.emit('friendDied');
}
else {
player.emit('enemyDied');
}
k ;
}
// 你可爱的队友已被击*
// 打的好呀,小帅哥
// 你可爱的队友已被击*
// 你可爱的队友已被击*
// 打的好呀,小帅哥
// 你可爱的队友已被击*
// 你可爱的队友已被击*
// 打的好呀,小帅哥
// 打的好呀,小帅哥
Why
优点
很好的实现事件推送,建立一套完备的触发机制
缺点
应用场景:JS中的addEventListener和Redux中的数据流模型
后续文章看到这里的朋友请给自己竖一个大指姆,你真棒!其余的设计模式将在后续文章中补全~
觉得有帮助的话别忘了点赞 收藏 关注哦,你的支持就是我前进的动力❥(^_-)~
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved