观察者模式
开发中中会遇到如此的事情:每个模块都只完成自己的功能,但是只有全部整合才是我们的目标。
这是最常见不过的例子,每个部分都必须按照逻辑顺序进行执行,也可能是分支选择的执行。
写一起太长,所以有了封装的代码块,类或者方法。
但是在整合过程中,有不免各种的
if...else
,switch...case
…也就是说,我们需要知道每个部分执行到了什么哪个部分,到了没有,执行完没有,我们总想知道。
我们总想知道,在远方的亲人如何了,在干什么,生病没有。
就好比一个瘫痪的儿子:
- 醒了要吃,但是不会收拾
- 吃了要睡,但是不能洗碗
- 睡觉之前,不能自己洗脚
所以我们总需要尽心的去服侍,想更好的去了解他,去帮助他,要更好的去观察他。
面对你喜欢的人,不也是如此么,我们总知道什么场景该做啥,却不知道何时去做。
listener
public class PersonListener {
private String name;
public PersonListener(String name){
this.name = name;
}
public void listenWakeUp(){
show("make lunch");
}
public void listenGoSleep(){
show("footbath");
}
public void listenEat(){
show("heat up water");
}
public void show(Object msg){
System.out.println(this.name + " : " + msg);
}
}
person
public class Person {
final public List<PersonListener> listeners = new ArrayList();
public void addListenr(PersonListener listener){
this.listeners.add(listener);
}
public void weakUp(){
show("person weak up");
listeners.forEach(listener -> listener.listenWakeUp());
}
public void goBead(){
listeners.forEach(listener -> listener.listenGoSleep());
show("person go to bed");
}
public void eat(){
show("person eating");
listeners.forEach(listener -> listener.listenEat());
}
public void show(Object msg) {
System.out.println(msg);
}
public static void main(String[] args) {
Person person = new Person();
person.addListenr(new PersonListener("godme"));
person.weakUp();
person.eat();
person.goBead();
}
}
就是这个样子,不过可能和大家想象的背道而驰
观感上是完全的背离,并不是主动的去观察
,而是被动的等通知
。
毕竟自己的事情自己最清楚,外部进行观察总是比自己进行通知更麻烦。
不过这毕竟是代码中,而且是自己管控的代码,如果是追求别人,只能主动了,入侵别人电脑,也只能自己扫描。
netty
中的观察者
public interface ChannelFuture extends Future<Void> {
Channel channel();
@Override
ChannelFuture addListener(GenericFutureListener<? extends Future<? super Void>> listener);
@Override
ChannelFuture addListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);
@Override
ChannelFuture removeListener(GenericFutureListener<? extends Future<? super Void>> listener);
@Override
ChannelFuture removeListeners(GenericFutureListener<? extends Future<? super Void>>... listeners);
@Override
ChannelFuture sync() throws InterruptedException;
@Override
ChannelFuture syncUninterruptibly();
@Override
ChannelFuture await() throws InterruptedException;
@Override
ChannelFuture awaitUninterruptibly();
boolean isVoid();
}
看看也能明白,它会在指定的时刻回调
触发,好像我们在监听一般。
Reactor
中的观察者
while(true){
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
selectionKeys.forEach(selectionKey -> {
try{
if(selectionKey.isAcceptable()){
...
}else if(selectionKey.isReadable()){
...
}
}
}catch (Exception e){
e.printStackTrace();
}
});
selectionKeys.clear();
}
NIO
的select
不陌生吧,我们何时知道它执行差不多了呢,也是因为通知
啊。
自己仿照一个
state
public enum State {
CONNECT,READ,WRITE
}
Channel
public class MyChannel {
private String name;
public State state;
public State listenState;
public MyChannel(String name){
this.name = name;
this.state = State.READ;
}
static void sleep(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void connect(){
new Thread(()->{
MyChannel.sleep();
state = State.CONNECT;
read();
}).start();
}
public void read(){
new Thread(()->{
MyChannel.sleep();
state = com.godme.listener.State.READ;
write();
}).start();
}
public void write(){
new Thread(()->{
MyChannel.sleep();
state = com.godme.listener.State.WRITE;
} ).start();
}
public void showState(){
System.out.println("channel["+this.name+"] is " + state + "ED");
}
}
为了模拟出状态的慢慢执行,必须开启线程进行异步通知。
否则的话,在单个线程当中是顺序阻塞的,状态跳转过程不会被其他线程观察到,必须异步。
Selector
public class Selector {
private final HashMap<State, HashSet<MyChannel>> stateGroup = new HashMap<>();
private final HashSet<MyChannel> triggerSet = new HashSet<>();
private static boolean trigger = false;
{
stateGroup.put(State.CONNECT, new HashSet<>());
stateGroup.put(State.READ, new HashSet<>());
stateGroup.put(State.WRITE, new HashSet<>());
new ListenThread().start();
}
public void register(State state, MyChannel channel){
channel.listenState = state;
stateGroup.get(channel.state).add(channel);
}
public HashSet<MyChannel> select() {
trigger = false;
while(!trigger){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return triggerSet;
}
class ListenThread extends Thread{
public ListenThread(){
this.setDaemon(false);
}
boolean notify = false;
@Override
public void run() {
while(true){
stateGroup.forEach((state, stateSet)->{
stateSet.forEach(channel -> {
if(channel.state != state){
stateSet.remove(channel);
stateGroup.get(channel.state).add(channel);
}
});
});
notify = false;
stateGroup.forEach((state, stateSet)->{
stateSet.forEach(channel->{
if(channel.state == channel.listenState){
triggerSet.add(channel);
stateGroup.get(channel.state).remove(channel);
notify = true;
}
});
});
if(notify){
trigger = true;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
这里也是异步的去检测状态,当状态和监听状态一致时,发出通知。
main
public class Main {
public static void main(String[] args) {
Selector selector = new Selector();
MyChannel channel = new MyChannel("godme");
selector.register(State.READ, channel);
channel.connect();
while(true){
HashSet<MyChannel> selectionKeys = selector.select();
selectionKeys.forEach(chanel->{
if(channel.state == State.CONNECT){
chanel.showState();
selector.register(State.READ, channel);
}else if(channel.state == State.READ){
chanel.showState();
selector.register(State.WRITE, channel);
}else if(channel.state == State.WRITE){
chanel.showState();
}
});
selectionKeys.clear();
}
}
}
仔细一看,是不是就很想NIO
的那一部分呢。
不过这只是理解所谓事件通知
,也就是观察者模式。
sync
channelFuture.channel().closeFuture().sync();
还记得
sync
么,可以参考一下select
public HashSet<MyChannel> select() { trigger = false; while(!trigger){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } return triggerSet; }
其实就是一直阻塞,等到通知以后再进行返回。
直接操作
Future
,有可能资源并没有准备好,会报错。但是当
Future
没准备好之前,sync
一直这样阻塞,准备好之后,发出通知。所以,
sync
获取的资源必定是已经就绪的(如果不出其他故障的话)。
观察者,就是事件通知(调用)
。
适配器器模式
ActionInterface
public interface ActionInterface {
public void hello();
public void play();
public void fuck();
}
ActionAdapeter
public class ActionAdapeter implements ActionInterface {
@Override
public void hello() {
}
@Override
public void play() {
}
@Override
public void fuck() {
}
}
Action
public class Action extends ActionAdapeter {
@Override
public void play() {
System.out.println("play");
}
}
如你所见,适配器模式在于只做想做的
。
就好比一个Listener
,我们定义的接口可以监听两百万个事件,我们怎么传入这么一个监听器呢?
全部实现不仅傻,而且稍微的变动就又得再写其他一百九十九万个方法?
利用继承
,复用父类中的方法,这样,我们甚至直接传一个匿名接口,只需要实现自己需要的监听即可。
一般来说,使用
Adapter
来实现默认操作,这个操作可能是空操作。然后子类就可以复用父类的实现,针对性的去进行单方法的复写(实现)。
层级结构而言,也是对接口的实现,可以泛型接收。
但是需要注意两点
- 声明的
Adapter
为了避免直接实例化,滥用产生歧义,最好是抽象类abstract
。- 接口中的特异化方法,
Adapter
最好不要实现,保留抽象,让子类进行操作填充。
netty
中的适配器
public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelRegistered();
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelUnregistered();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelInactive();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.fireChannelRead(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelReadComplete();
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
ctx.fireUserEventTriggered(evt);
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelWritabilityChanged();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.fireExceptionCaught(cause);
}
}
这么多方法,实现到死啊,非得用适配器不可。
为什么?因为我懒。
模板方法
public class Main {
public static void main(String[] args) {
System.out.println("before");
doSomething();
System.out.println("after");
}
public static void doSomething(){
System.out.println("doSomething");
}
}
其实就是这个样子,把操作收归于一处,然后进行调用。
但是具体动作是反向调用,逆向填充
。
Parent
public abstract class Parent {
abstract public void hello();
public void doSomething(){
System.out.println("see somebody");
hello();
System.out.println("do something");
}
}
Child
public class Child extends Parent{
@Override
public void hello() {
System.out.println("你才可爱,你们全家都可爱");
}
}
main
public class Main {
public static void main(String[] args) {
new Child().doSomething();
}
}
看起来和观察者模式
一样,都是关键时刻回调的样子。
的确,差别不大,或者说都是回调,但是,作用场景完全不同
- 观察者的回调在于时机,是发生什么的时候的附加处理,像
try...catch
的样子 模板方法
的回调注重的就是回调操作本身,周边都是为他而服务
不仅侧重不一样,而且,顺序也不一样:
- 观察者是向下回调
- 模板方法是向上填充回调
更常见的例子就是数据库连接一样
public void deal(){
Connect connect = Connect.open();
deal(connect);
connect.close();
}
abstract public void deal(Connect connect);
如果这个样子,我们以后处理连接问题,就完全不用去考虑关闭,也不用考虑连接获取问题。
把重心放在具体业务,一心一意做业务。
当然,如果用观察者模式
的话,就是做完业务以后发通知进行回调了,但是不显得太麻烦了么?重点搞错了吧。
观察者模式用于不太紧密的的操作之间,属于平级的调用。
模板方法用于比较紧密的操作的流程制定,把基础准备和收尾工作做好,留下核心操作待填充。
netty
中的模板方法
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I imsg = (I) msg;
channelRead0(ctx, imsg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
ReferenceCountUtil.release(msg);
}
}
}
看到ChannelRead0
了么,这就是SimpleChannelInboundHandler
中的使用了。
不仅泛型给我们直接转化过了,而且还自动给我们回收ReferenceCountUtil.release(msg)
哦。
读取的
RecvBuffer
经过handler
处理以后,实际上仍然在内存当中。正如我们确定好不需要的对象需要
person = null
来通知释放,给JVM
一个明确的标记。
ReferenceCountUtil.release(msg);
就是这么一个明确标记,帮我们递减计数哦。
小结
目前已经出现了如下设计模式
- 装饰器(
IO
) Reactor
- 观察者
- 适配器
- 模板方法