无处不在的耦合

通常你写的一个React组件,如果需要和别的组件发生交互,那么你会怎么做?

1
2
3
4
5
6
7
8
9
public componentDidMount(): void {
this.refs.mycom.test();
}

public render() {
return (
<MyCom ref="mycom" />
);
}

这种做法相对来说比较普遍,也比较合情合理,那么如果MyCom组件执行了什么操作之后需要通知到父组件呢?

1
2
3
4
5
6
7
8
9
public onNotify(): void {

}

public render() {
return (
<MyCom notify={ this.onNotify.bind( this ) } ref="mycom" />
);
}

很多时候我们会选择传进去一个回调函数,当MyCom需要发送通知的时候再调用props.notify

虽然这种方式能实现我们的需求,但也仅仅是实现而已,你会发现这种方式并不是很优雅,而且当你的回调函数多了之后,那简直就是噩梦,后来来使用这个组件的人完全不知道需要传哪些回调函数进去,也不知道哪些回调是可选的,哪些是必选的。

protocol

ProtocolOc里面的概念,类似Java里面的Interface,和TypeScript中的Interface也比较类似,其中有一个用处就是用于两个类之间的通信解耦,而delegate更多的是一种约定俗成的规则,看看具体怎么用:

首先我有一个A类,用

A.h

1
2
3
4
5
6
7
8
9
@protocol ADelegate
- (void) methodFromA;
@end

@interface A : NSObject
// 声明委托变量
@property (assign, nonatomic) id<ADelegate> delegate;
- (void) test;
@end

A.m

1
2
3
4

// 在A类的实现文件里面,在需要进行回调的时候,只需要调用当前实例的delegate对象,而不用理会到底是谁调用了自己,而且对方肯定实现了delegate指向的protocol
// 需要的时候调用
[self.delegate methodFromA];

此时,我有一个B类,需要调用A类,同时需要监听回调函数:

B.h

1
2
3
4
5
6
#import "A.h"

// 这里实现了Adelegate Protocol
@interface B : NSObject <ADelegate>
...
@end

B.m

1
2
3
4
5
6
7
8
9
10
11
- (void) someMethod {
A *aInstance = [[A alloc] init];
// 这里把当前实例赋值给A类实例的delegate属性
aInstance.delegate = self;
}

// 实现了ADelegate
- (void) methodFromA {
当A类调用self.delegate.methodFromA时,就可以在这里执行你自己的代码了
// ...
}

TypeScript中的delegate

上面可以看到其实delegate的使用非常简单,只是一种约定的规则,同时delegate其实也是一种设计模式,在JS中其实也可以使用,只是JS是弱类型语言,无法通过编译器来约束一些行为,所以依赖于TypeScript的强类型,我们也可以来使用一下delegate

组件A

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export interface ADelegate {
methodFromA();
}

export class A extends React.Component<any, any> {
public delegate: ADelegate;

public render() {

// 调用delegate的方法
this.delegate.methodFromA();

return (
<div></div>
);

}

}

组件B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { A, ADelegate } from 'A';

// 这里实现了Interface: ADelegate
export class B extends React.Component<any, any> implements ADelegate {
// 从A实现的回调。
public methodFromA(): void {

}

public componentDidMount(): void {
const aCom: A = <A>this.refs.acom;
aCom.delegate = this;
}

public render() {
return (
<A ref="acom" />
);
}
}

效果

上面的代码可以看到,A组件面向的不是调用他的组件,而是他自己声明的delegate,完全无需关心是谁在调用自己,在什么场景下调用自己

而B组件也完全不用关心A组件的具体实现,只要实现A组件提供的Interface:ADelegate,从中选择自己需要的方法即可,两者实现解耦,面向delegate编程。