五大设计原则
S-单一职责原则
- 一个程序只做好一件事 (能过于复杂就差分开,每个部分保持独立)
开发封闭原则
- 对扩展开发,对修改封闭(需求时,扩展新代码,而非修改已有代码)
李氏置换原则
- 子类能覆盖父类(父类能出现的地方子类就能出现,JS中使用较少,弱类型,承使用较少)
I-接口独立原则
- 保存接口的单一独立,避免出现“胖接口”(类似与单一职责原则,这里更关注接口)
D-依赖倒置原则
- 面向接口编程,依赖于抽象而不依赖于具体(使用方只关注接口而不关注具体类的实现,JS中使用较少,没有接口,弱类型)
设计模式包含三种类型
创建型
- 工厂模式(工厂方法模式,抽象工厂模式,建造者模式)/单例模式/原型模式
结构型
- 适配器模式/装饰器模式/代理模式/外观模式/桥接模式/组合模式 /享元模式
行为型1
- 策略模式/迭代器模式/模板方法模式/职责连模式/观察者模式/命令模式
行为型2
- 备忘录模式/中介者模式/状态模式/解释器模式/访问者模式
工厂模式
- 一般是将new操作单独封装,遇到nwe时就要考虑是否用了工厂模式
class Product{
constructor(name){
this.name = name
}
init(){
alert("init")
}
fn1(){
alert("fn1")
}
}
class Creator{
create(name) {
return new Product(name)
}
}
let crateor = new Creator("create")
let p = crateor.create("cr1")
p.init()
p.fn1()
create相当于一个工厂,调用product零散组件
其他应用场景有 jQury 的$("div"), React.createElement
这种方式符合 构造函数和创建者和分离,符合开发封闭原则
单例模式
- 系统中被唯一使用,一个类只能有一个实例 在java中
public class SingleObj{
private SingleObj(){
}
private SingleObj instance = null;
public SingleObj getInstance() {
// 这里面只能有一个实例,singleobj外部是不能实例化的
if(instance==null){
instance = new SingleObj();
}
return instance;
}
public void login(user,pwd) {
//..
}
}
在js中可以这样写
class SingleObj{
login() {
console.log("登录");
}
}
SingleObj.getData = (function() {
let data;
return function(){
if(!data) {
data = new SingleObj();
}
return data;
}
})();
let obj1 = SingleObj.getData();
obj1.login();
let obj2 = SingleObj.getData();
obj2.login();
console.log(obj1===obj2); // 这里的实例是同一个
let obj3 = new SingleObj();
obj3.login();
console.log(obj2===obj3); // 这里就不是同一个实例了
/*
登录
登录
true
登录
false
*/
场景:jQuery中只有一个$, 登录框,购物车,个人信息,vux和redux
适配器模式
就接口格式和使用者不兼容,中间加一个适配器转换接口
add类是最开始的类,为了不改变他里面的内容,创建个Target类,对其包装道,最后调用者使用Target类里面的方法就行了,
class Add{
beforeData() {
return "原始数据"
}
}
class Target{
constructor(){
this.add = new Add()
}
afterData() {
const addData = this.add.beforeData()
return `${addData}-新数据`
}
}
const targets = new Target()
const rs = targets.afterData()
console.log(rs);
// 原始数据-新数据
这种模式在vue的Computed 中有使用,对于原始dada数据,为了不改变他,在Computed里面做下处理,重新返回一个值,就可以用了。
装饰器模式
- 为对象添加新功能,不改变其原有的结构和功能
class Circle{
draw(){
console.log("画园");
}
}
class Decorator{
constructor(circle) {
this.circle = circle;
}
draw() {
this.circle.draw();
this.setBorder();
}
setBorder() {
console.log("画边框");
}
}
const cr = new Circle();
cr.draw();
const dc = new Decorator(cr);
dc.draw();
/*
画园
画园
画边框
*/
es6的装饰器也是同样道理
function readonly(target, name, descriptor) {
descriptor.writable = false;
return descriptor;
}
// 给a 属性和name方法加上只读的装饰器,这两个就不允许被改变了
class Person{
@readonly
a = "p"
constructor(){
this.a = "a";
}
@readonly
name() {
this.a = "aa";
return `${this.a}`
}
}
let p = new Person()
console.log(p.name())
p.name = "aa"
代理模式
使用者无权访问目标对象,需要通过中介来访问
class Read{
constructor(name) {
this.name = name;
this.load();
}
load(){
console.log("加载"+name);
}
show() {
console.log("展示" + name);
}
}
// 通过代理类,访问里面的数据
class ProxyRead{
constructor(name) {
this.read = new Read(name);
}
show(){
this.read.show();
}
}
let proxyRd = new ProxyRead("张");
proxyRd.show();
外观模式
为子系统中的一组接口提供了一个高层接口,使用者使用这个高层接口
不同的参数数量可以达到不同的效果
但是外观模式不符合单一职责原则和开放封闭原则,因此需要谨慎使用
发布订阅模式
案例
class Subject{
constructor() {
this.state = 0;
this.observeArr = [];
}
getState() {
return this.state;
}
setState(state) {
this.state = state;
this.notifyAllObserve();
}
notifyAllObserve() {
this.observeArr.forEach(observe=>{
observe.update();
})
}
attach(observe) {
this.observeArr.push(observe);
}
}
class Observe{
constructor(name, subject) {
this.name = name;
this.subject = subject;
this.subject.attach(this);
}
update() {
console.log(`${this.name} update, state ${this.subject.getState()}`);
}
}
let s = new Subject();
let o = new Observe("1", s);
let a= new Observe("2", s);
let b = new Observe("3", s);
s.setState("as"); // s类添加一个状态时,其他的观察者都能监听到
s.setState("ab");
/*
1 update, state as
2 update, state as
3 update, state as
1 update, state ab
2 update, state ab
*/
平时的使用场景有jq的绑定事件,一般我们是一个dom绑定一个事件,当我们一个dom绑定多个click事件的时候,当点击的时候其他事件也会触发。 还有nodejs的流读取文件的时候监听data和end事件,
迭代器模式
- 顺序访问一个集合,使用者无需知道集合的内部结构 比如jq中dom集合,可以使用$.each 来循环遍历里面的dom进行操作。还有es6的Iterator迭代器,其next函数方法可以按顺序迭代
function each(data) {
let ite = data[Symbol.iterator]();
let item = { done: false };
// 当iterator迭代完成了,done为true,
while (!item.done) {
item = ite.next();
if (!item.done) { console.log(item.value) }
}
};
each(["1", 2, 58, 4, 4, 12]);
/*
1
2
58
2* 4
12
*/
for of就是Iterator的实现
状态模式
- 一个对象有状态变化,每次状态变化都会触发一个逻辑,不能总是用if else来控制 案例:
class State{
constructor(color){
this.color = color;
}
handle(context) {
console.log("状态" + this.color);
context.setState(this);
}
}
class Context{
constructor() {
this.state = null;
}
getState() {
return this.state;
}
setState(state) {
this.state = state;
}
}
let context = new Context();
const a = new State("state1");
const b = new State("state2");
a.handle(context);
console.log(context.getState());
b.handle(context);
console.log(context.getState());
/*
状态state1
State {color: "state1"}color: "state1"__proto__: Object
状态state2
State {color: "state2"}
*/
状态主体context和状态state本身区分开,让状态主体可以获取不同状态 这里可以实际用的时候可以使用一个插件库javascript-state-machine状态机
<div id="btn1"></div>
<script src="./src/state-machine.js"></script>
<script>
let fsm = new StateMachine({
init: "收藏", // 初始化的时候的状态
transitions: [{ // 几种状态变化
name: "doStore",
from: "收藏",
to: "取消收藏"
}, {
name: "deleteStore",
from: "取消收藏",
to: "收藏"
}],
methods: {
onDoStore: function () {
alert("收藏成功");
updateState(); // 通知状态
},
onDeleteStore: function () {
alert("取消收藏");
updateState();
}
}
})
let $btn = document.querySelector("#btn1")
$btn.addEventListener("click", function () {
if (fsm.is("收藏")) {
fsm.doStore();
} else {
fsm.deleteStore();
}
});
function updateState() {
console.log("状态已经改变");
$btn.innerHTML = fsm.state;
}
// 初始化btn状态,
updateState();
</script>
原型模式
在js中new一个对象开销是比较大的,一般可以复制一个原型来新生成一个对象使用,消耗就比较小了。一般会使用Object.create()函数来创建
let prototypes= {
getName: function () {
return this.first +" " + this.last
},
getName: function () {
console.log("hello");
}
}
let x = Object.create(prototypes);
x.first = "1";
x.last = "2";
console.log(x.getName());
/*
hello
*/
function obj(name) {
this.name = name;
function setName() {
this.name = name;
console.log("setName");
}
}
obj.prototype.getName = function () {
console.log("getName:" + this.name);
}
let a = new obj("xx");
let x = Object.create(a);
x.getName();
/*
getName:xx
*/