0
点赞
收藏
分享

微信扫一扫

基于Angular+BootStrap+SpringBoot简单的购物网站

炽凤亮尧 11-28 23:00 阅读 3

策略设计模式

先写几句废话:其实在日常开发中,「设计模式」通常在不知不觉间已经被用了不少了,只是我们或许没察觉。比如通过插槽来增强组件的功能,这涉及到「装饰设计模式」;lodash或者jQuery的使用我觉得甚至算得上使用了「单例设计模式」;Vue2常见的$eventBus的使用是典型的「观察者模式」……

策略模式的核心思想是将算法(或者说功能)封装到不同的策略类中,以便根据具体的需要选择合适的策略,从而使得算法的变体可以独立于使用它的客户端而变化。

通常在借助策略设计模式实现的模块中,这种设计模式一般有3个参与者:抽象的策略类,具体的策略类、策略上下文。

策略设计模式在React里面用来做组件内部的权限控制特别有用。它不只是可以让你少写很多if_else语句,而且可以实现根据不同的策略分发不同的组件的操作,这种逻辑在Vue里面的实现大多数用的是v-if指令。项目一大,v-if维护起来多少有点痛苦 (不是踩一捧一,只是在上一家实习公司参与项目的时候的感受)

考虑一个很常见的场景:实现一张记录了本系统所有用户的Table,每一行要根据本行的用户角色,提供不同的交互和组件:

class User {
	constructor(name, age, address, roleType = 'user') {
		this.name = name;
		this.age = age;
		this.address = address;
		this.roleType = roleType;
	}
	setRoleType(value) { this.roleType = roleType; }
	getName() { alert(`My name is ${this.name}`) }
}

接着实现各个具体的策略。以下demo尽量不违背策略设计模式的原则🕶️。

type CallBackMap = Record<string, (...args: any[]) => any>
// 抽象的策略类
interface Strategy {
	getColorOfNameCell(): string;
	getContentOfActionsCell(user: User, callbacks: CallBackMap): JSX.Element; 
}
// 具体的策略类-管理员角色
class AdminStrategy implements Strategy {
	getColorOfNameCell() { return 'red' }
	getContentOfActionsCell(user: User, callbacks: CallBackMap) {
		return (
			<>
				<button onClick={() => callbacks.setRoleType(user, "user")}>
        			权限降级
        		</button>
        		<button onClick={() => callbacks.getName(user)}>查看详情</button>
				<button>禁用账号</button>
			</>
		)
	}
}
// 具体的策略类-用户角色
class UserStrategy implements Strategy {
	getColorOfNameCell() { return 'blue' }
	getContentOfActionsCell(user: User, callbacks: CallBackMap) {
		return (
			<>
				<button onClick={() => callbacks.setRoleType(user, "admin")}>
        			权限升级
        		</button>
        		<button onClick={() => callbacks.getName(user)}>查看详情</button>
				<button>禁用账号</button>
			</>
		)
	}
}
// 具体的策略类-默认角色
class DefaultStrategy implements Strategy {
	getColorOfNameCell() { return 'gray' }
	getContentOfActionsCell(user: User, callbacks: CallBackMap) {
		return ( <div>默认角色</div> )
	}
}

然后再在组件里面使用策略设计模式:

const Page = () => {
	const [userData, setUserData] = useState<Array<User>>([
	  new User("张三", 28, "北京市朝阳区", "admin"),
	  new User("李四", 22, "上海市浦东新区", "user"),
	  new User("王五", 35, "广州市天河区", "admin"),
	  new User("赵六", 30, "深圳市南山区", "user"),
	  new User("钱七", 40, "杭州市西湖区", "user"),
	]);
	const setRoleType = (user: User, roleType) => {
		user.setRoleType(newRoleType);
		setUserData([...userData]);
	};
	const getName(user: User) => {
		user.getUserName();
	}
	const getStrategy = (roleType) => {
	  switch (roleType) {
	    case "admin":
	      return new AdminStrategy();
	    case "user":
	      return new UserStrategy();
	    default:
	      return new DefaultStrategy();
	  }
	};
	return (
		<table>
			<thead> {/* 日常偷懒 */} </thead>
			<tbody>
			  {userData.map((user) => {
			  	// 先根据roleType来获取所谓的策略上下文
			    const strategyContext = getStrategy(user.roleType);
			    return (
			      <tr key={user.name}>
			        <td>{user.name}</td>
			        <td>{user.age}</td>
			        <td>{user.address}</td>
			        <td style={{ color: strategyContext.getColorOfNameCell() }}>
			          {user.roleType.toUpperCase()}
			        </td>
			        <td>
			          {strategyContext.getContentOfActionsCell(user, {
			            getName,
			            setRoleType,
			          })}
			        </td>
			      </tr>
			    );
			  })}
			</tbody>;
		</table>
	)
}

真的好用,伟大无需多言。之前被抬杠说每一次map都要创建一个策略上下文太蠢了,说消耗性能。我都用设计模式了我还在意性能?而且一根指针女也女马白勺能消耗啥性能?????


抽象工厂设计模式

在这里插入图片描述
说来说去感觉不就是组件的封装与使用吗?顶多加上主题色的配置和通过函数的调用来使用组件。之前在公司有一个远古项目使用React+Js,那哥们用抽象工厂来处理整个系统的页面组件里面的代码,当时没仔细看,回想起来可能每一个交互的函数都被他封装到「某一个具体的工厂的某一个产物」里面去了。不知道那兄弟是怎样抽离那些逻辑的,反正我Leader后来维护的时候他说简直想死。

现假设我们想要实现一个简单的主题切换功能,不同的主题可能有不同的按钮和输入框样式。我们可以使用抽象工厂模式来创建不同主题的 UI 组件。有四个参与者:主题接口、具体主题、抽象工厂和具体工厂。

// 主题接口
class Button {
    render() {
        throw new Error('This method should be overridden!');
    }
}
class Input {
    render() {
        throw new Error('This method should be overridden!');
    }
}
// 具体主题:浅色主题
class LightButton extends Button {
    render() {
        return <button style={{ backgroundColor: '#fff', color: '#000' }}>Light Button</button>;
    }
}
class LightInput extends Input {
    render() {
        return <input style={{ backgroundColor: '#fff', color: '#000' }} placeholder="Light Input" />;
    }
}
// 具体主题:深色主题
class DarkButton extends Button {
    render() {
        return <button style={{ backgroundColor: '#333', color: '#fff' }}>Dark Button</button>;
    }
}
class DarkInput extends Input {
    render() {
        return <input style={{ backgroundColor: '#333', color: '#fff' }} placeholder="Dark Input" />;
    }
}
// 抽象工厂接口
class ThemeFactory {
    createButton() {
        throw new Error('This method should be overridden!');
    }
    createInput() {
        throw new Error('This method should be overridden!');
    }
}
// 具体工厂:浅色主题工厂
class LightThemeFactory extends ThemeFactory {
    createButton() { return new LightButton() }
    createInput() { return new LightInput() }
}
// 具体工厂:深色主题工厂
class DarkThemeFactory extends ThemeFactory {
    createButton() { return new DarkButton() }
    createInput() { return new DarkInput() }
}
// App 组件
class App extends React.Component {
    state = { isLightTheme: true };
    toggleTheme = () => {
        this.setState(prevState => ({
            isLightTheme: !prevState.isLightTheme,
        }));
    };
    render() {
        const factory = this.state.isLightTheme ? new LightThemeFactory() : new DarkThemeFactory();
        const ButtonComponent = factory.createButton();
        const InputComponent = factory.createInput();
        return (
            <div>
                <h1>{this.state.isLightTheme ? 'Light Theme' : 'Dark Theme'}</h1>
                {ButtonComponent.render()}
                {InputComponent.render()}
                <button onClick={this.toggleTheme}>Toggle Theme</button>
            </div>
        );
    }
}
export default App;

观察者设计模式

无需多言,直接放手撸的代码:

class HMEmmiter {
	#handlers = {}
	$on(event, callback) {
		if (this.#handlers[event] === undefined) {
			this.#handlers[event] = []
		}
		this.#handlers[event].push(callback)
	}
	$emit(event, ...args) {
		const funs = this.#handlers[event] || []
		funs.forEach(callback => { callback(...args) })
	}
	$off(event) {
		this.#handles[event] = undefined
	}
	$once(event, callback) {
		this.$on(event, (...args) => {
			callback(...args)
			this.$off(event)
		})
	}
}

面🦐🍺被考到了,还好写的出来


高阶思想

上述的分析来自Chat,但是一番实践下来最大的感受还是高阶函数,或者说高阶组件的使用是保护了核心代码的干净、简洁、强大,很像rollup、webpack和eggjs这些应用,官方只开发这些应用的核心代码,然后把插件的开发(除了官方的那几个插件)权利都分享给社区:我只维护我最值钱、最整洁、最正确、最干净的核心,剩下的要怎么搞花里胡哨的新功能各位随便开发,我无所谓🕶️

举报

相关推荐

0 条评论