惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
Latest news
Latest news
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
The Hacker News
The Hacker News
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
Cisco Talos Blog
Cisco Talos Blog
Hacker News: Ask HN
Hacker News: Ask HN
Simon Willison's Weblog
Simon Willison's Weblog
W
WeLiveSecurity
H
Hacker News: Front Page
L
LINUX DO - 热门话题
T
Tor Project blog
N
News | PayPal Newsroom
S
Secure Thoughts
小众软件
小众软件
Recent Commits to openclaw:main
Recent Commits to openclaw:main
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
AWS News Blog
AWS News Blog
Security Latest
Security Latest
MyScale Blog
MyScale Blog
Webroot Blog
Webroot Blog
Cyberwarzone
Cyberwarzone
Engineering at Meta
Engineering at Meta
腾讯CDC
T
Tailwind CSS Blog
SecWiki News
SecWiki News
博客园 - 司徒正美
Microsoft Azure Blog
Microsoft Azure Blog
P
Palo Alto Networks Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
V2EX
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
B
Blog RSS Feed
大猫的无限游戏
大猫的无限游戏
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Martin Fowler
Martin Fowler
P
Proofpoint News Feed
GbyAI
GbyAI
C
Check Point Blog
博客园_首页
PCI Perspectives
PCI Perspectives
月光博客
月光博客
TaoSecurity Blog
TaoSecurity Blog
aimingoo的专栏
aimingoo的专栏
酷 壳 – CoolShell
酷 壳 – CoolShell
IT之家
IT之家

Jiajun的技术笔记

你好,2026! TiDB 源码阅读(六):TiDB Coprocessor 源码解析 性能优化的核心思想 TiDB 源码阅读(五):索引 TiDB 源码阅读(四):AST、逻辑计划、物理计划 CockroachDB Serverless Architecture podman 无故退出 Cursor Control-L (CTRL-L) Keyboard Shortcuts in Terminal Replace docker with podman Using xmonad with xfce4 A RC script for freebsd frpc 自己动手写一个k8s controller AI 会取代你的(编程)岗位吗? 自建DERP服务器提升Tailscale连接速度(使用Nginx转发) 自动升级Docker容器 再读《程序员修炼之道-从小工到专家》 让浏览器下载文件 再读《软件随想录》/《黑客与画家》/《软技能》 HTTP 压力测试中的 Coordinated Omission 2的补码 编程语言中的 context 是什么? flutter macOS 构建出错 Flatpak 使用小记 Golang CAS 操作是怎么实现的 PostgreSQL 当MQ来使用 Clash 结合 工作VPN 的网络设计 使用 PostgreSQL 搭建 JuiceFS PostgreSQL 配置优化和日志分析 有GitHub Copilot?那就可以搭建你的ChatGPT4服务 窗口函数的使用(以PG为例) 读《为什么学生不喜欢上学》 OpenAI Prompt Engineering 摘录和总结 读《打造真正的新产品》 VueJS 总结 Linux 自动挂载 alist 提供的webdav FreeBSD 使用 vm-bhyve 安装Debian虚拟机 FreeBSD 和 Linux 网卡聚合实现提速 GPT 帮我搞定了时区转换问题 长任务系统如何处理? macOS/Linux 编译 InputLeap 使用开源软KVM - synergy-core 解决 macOS 终端hostname一直变化问题 KVM 共享 Intel 集成显卡 PromQL 备忘 读《格鲁夫给经理人的第一课》 读《打开心智》 为什么要把复杂的联表操作拆成多个单表查询? 红包系统的设计 MySQL Index Condition Pushdown Optimization Go mod 简明教程 OpenWRT 使用 Android/iOS USB 网络 搭建旁路由 Golang gRPC 错误处理 编写可维护的单元测试代码 OAuth 2 详解(六):Authorization Code Flow with PKCE OAuth 2 详解(五):Device Authorization Flow OAuth 2 详解(三):Resource Owner Password Credentials Grant OAuth 2 详解(四):Client Credentials Flow OAuth 2 详解(二):Implict Grant Flow OAuth 2 详解(一):简介及 Authorization Code 模式 ElasticSearch 学习笔记 三种git流程以及发版模型 错误处理实践 权限模型(RBAC/ABAC) OIDC(OpenID Connect) 简介 任务队列简介 PostgreSQL 操作笔记 使用Drone CI构建CI/CD系统 Golang migrate 做数据库变更管理 使用PostgreSQL做搜索引擎 Nginx 源码阅读(三): 连接池、内存池 Nginx 源码阅读(二): 请求处理 Nginx 源码阅读(一): 启动流程 Go 泛型简明教程 KVM 显卡穿透给 Windows 使用 HTTP Router 处理 Telegram Bot 按钮回调 使用反射(reflect)对结构体赋值 GIN 是如何绑定参数的 你好 2022(2021 年终总结) 用Go导入大型CSV到PostgreSQL 使用 OpenWRT 搭建软路由 使用软KVM切换器 barrier 共享键鼠 SQL 防注入及原理 使用 gomock 测试 Go 代码 gevent不是黑魔法(二): gevent 实现 gevent不是黑魔法(一): greenlet 实现 用 entgo 替代 gorm 应用内使用crontab不是那么方便 单测时要不要 mock 数据库? Sentry 自建指南 用selenium完成自动化任务 用闲置的安卓手机做垃圾电话短信过滤 推荐三个时间管理工具 一次事故反思 当JS遇到uint64:JS整数溢出问题 SQLite3 存储以及ACID原理 Redis源码阅读:pub/sub实现 Redis源码阅读:zset实现 Redis源码阅读:bitmap 位图的运算 Redis源码阅读:set是怎么做交并集运算的?
设计模式(1)- 深入浅出设计模式 阅读笔记
2017-01-21 · via Jiajun的技术笔记

将会使用Java和Python。其中Java实现可能比较业余,但是仍然使用Java, 因为很多设计模式在Java中更容易看清楚。

策略模式,观察者模式,装饰器模式,工厂模式,单例模式。

策略模式

策略模式其实就是一句话“针对接口编程,而不是针对实现编程”。接口是在Java中 可以是对应interface也可以是abstract类。接口其实是一个抽象概念。例如,c语言虽然 语法上不支持面向对象编程,但是其头文件的声明就相当于接口。

我们先来看Python版本的策略模式:

class SoundMixin:
    def sound(self):
        raise NotImplemented()


class DogSoundMixin(SoundMixin):
    def sound(self):
        print("wang wang...")


class DuckSoundMixin(SoundMixin):
    def sound(self):
        print("gua gua...")


class Animal:
    def make_noise(self):
        self.sound()


class Dog(DogSoundMixin, Animal):
    pass


class Duck(DuckSoundMixin, Animal):
    pass


if __name__ == "__main__":
    Dog().make_noise()
    Duck().make_noise()

运行一下:

$ python interface.py 
wang wang...
gua gua...

然后是Java版本:

// Sound.java
public interface Sound {
    public void sound();

}

// Animal.java
public abstract class Animal {
    abstract void sound();

    public void makeNoise() {
        sound();
    }
}

// Dig.java
public class Dog extends Animal implements Sound {
    public void sound() {
        System.out.println("wang wang...");
    }
}

// Duck.java
public class Duck extends Animal implements Sound {
    public void sound() {
        System.out.println("gua gua...");
    }
}

// Main.java
public class Main {
    public static void main(String[] args) {
        Animal animal;

        animal = new Dog();
        animal.makeNoise();

        animal = new Duck();
        animal.makeNoise();
    }
}

运行一下:

$ javac *.java && java Main 
wang wang...
gua gua...

观察者模式

观察者模式,就是消息订阅。消息提供者能够提供接口动态注册和注销,当消息到来, 消息提供者就会通知订阅者。典型的例子就是epoll,我们注册我们想要监听的事件, 例如描述符可读,当一个描述符可读时,epoll就会通知我们(这个例子也许不是特别 明显)。

我们先用Python实现一个:

class Observer:
    def __init__(self):
        self.callbacks = {}

    def register(self, name, func):
        self.callbacks[name] = func

    def cancell(self, name):
        if name in self.callbacks:
            self.callbacks.pop(name)

    def notify(self, event):
        for name, callback in self.callbacks.items():
            callback(name, event)


def callback(name, event):
    print("name(%s) with event: %s" % (name, event))


if __name__ == "__main__":
    observer = Observer()
    observer.register("marry", callback)
    observer.register("jack", callback)
    observer.register("lucy", callback)

    observer.notify("eat")

    observer.cancell("jack")
    observer.notify("sleep")

运行结果:

name(marry) with event: eat
name(jack) with event: eat
name(lucy) with event: eat
name(marry) with event: sleep
name(lucy) with event: sleep

再来Java:

// Observerable.java
import java.util.ArrayList;


public class Observerable {
    private ArrayList<Callback> observerables;
    public Observerable() {
        observerables = new ArrayList<Callback>();
    }

    public void register(Callback callback) {
        observerables.add(callback);
    }

    public void cancell(Callback callback) {
        int index = observerables.indexOf(callback);
        if (index >= 0) {
            observerables.remove(index);
        }
    }

    public void runAllCallbacks() {
        Callback callback;
        for (int i = 0; i < observerables.size(); i++) {
            callback = observerables.get(i);
            callback.callback("notifying " + i);
        }
    }
}

// Callback.java
public interface Callback {
    public void callback(String s);
}

// EatCallback.java
public class EatCallback implements Callback {
    public void callback(String s) {
        System.out.println("eat callback been called");
    }
}

// Main.java
public class Main {
    public static void main(String[] args) {
        Observerable observerable = new Observerable();

        observerable.register(new EatCallback());
        observerable.register(new EatCallback());
        observerable.register(new EatCallback());

        observerable.runAllCallbacks();
    }
}

运行一下:

$ javac *.java && java Main
eat callback been called
eat callback been called
eat callback been called

装饰器模式

装饰器模式,就是要在原有的函数上包装一层,让它能做更多的事情,但是却不用修改 原来的函数的代码。如果用Python的同学,相信无人不知无人不晓装饰器的威力,例如:

from contextlib import contextmanager


@contextmanager
def print_tag(tag):
    print("<%s>" % tag)
    yield
    print("</%s>" % tag)


with print_tag("body"):
    print("html body")
$ python decorator.py 
<body>
html body
</body>

然后再上Java实现:

// Origin.java
public class Origin {
    private String description = "Origin";

    public String getDescription() {
        return description;
    }
}

// Decorator.java
public class Decorator {
    Origin origin;

    public Decorator(Origin origin) {
        this.origin = origin;
    }

    public String getDescription() {
        return origin.getDescription() + " -> Decorator";
    }
}

// Main.java
public class Main {
    public static void main(String[] args) {
        Origin origin = new Origin();
        Decorator decorator = new Decorator(origin);
        System.out.println(decorator.getDescription());
    }
}

运行一下:

$ javac *.java && java Main
Origin -> Decorator

工厂模式,抽象工厂

工厂模式和抽象工厂的差别在于,一个使用继承来实现,是一种金字塔关系,超类在上。 而抽象工厂虽然具体实现是金字塔关系,但是实际使用的时候只需要指定接口,所以 总体看来是一个倒金字塔关系。Python上不是很能看得出来,但是Java版本的很明显。 我们举个例子,现在有NFS和ISCSI的库供我们使用,我们希望能够做到指定 type就 返回给我们对应的实例。

先来Python版本:

TYPE_NFS = "nfs"
TYPE_ISCSI = "iscsi"


class Storage:
    pass


class NFS(Storage):
    pass


class ISCSI(Storage):
    pass


class StorageFactory:
    @staticmethod
    def instance(type):
        return NFS() if type == TYPE_NFS else ISCSI()


if __name__ == "__main__":
    print(StorageFactory.instance(TYPE_NFS).__class__)
    print(StorageFactory.instance(TYPE_ISCSI).__class__)

运行一下:

$ python factory.py 
<class '__main__.NFS'>
<class '__main__.ISCSI'>

再来Java:

// Storage.java
public interface Storage {
    public String getName();
}

// NFS.java
public class NFS implements Storage {
    public String getName() {
        return "it's NFS";
    }
}

// ISCSI.java
public class ISCSI implements Storage {
    public String getName() {
        return "it's ISCSI";
    }
}

// StorageFactory.java
public class StorageFactory {
    public Storage instance(String type) {
        if (type.equals("nfs")) {
            return new NFS();
        } else {
            return new ISCSI();
        }
    }
}

// Main.java
public class Main {
    public static void main(String[] args) {
        StorageFactory storageFactory = new StorageFactory();
        System.out.println(storageFactory.instance("nfs").getName());
        System.out.println(storageFactory.instance("iscsi").getName());
    }
}

运行一下:

$ javac *.java && java Main
it's NFS
it's ISCSI

上面的Java代码是抽象工厂,其优点是简洁,调用类无需修改代码即可运行,但是每次 增加新的存储类型的时候,都需要修改一下instance方法。而工厂方法则无需如此:

// Storage.java
public abstract class Storage {
    public abstract String getName();
    public static Storage instance(String type) {
        return null;
    }
}

// NFS.java
public class NFS extends Storage {
    public String getName() {
        return "it's NFS";
    }

    public static Storage instance(String type) {
        return new NFS();
    }
}

// ISCSI.java
public class ISCSI extends Storage {
    public String getName() {
        return "it's ISCSI";
    }

    public static Storage instance(String type) {
        return new ISCSI();
    }
}

// Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println(NFS.instance("nfs").getName());
        System.out.println(ISCSI.instance("iscsi").getName());
    }
}

所以工厂模式和抽象工厂的区别在于,工厂模式由子类决定具体实例化哪个类,而 抽象工厂自己决定初始化哪个类。

单例模式

单例模式是在开发中经常能用到的。单例,名字比较官方,如果改成“全局唯一”, 那就瞬间更好理解了。例如我们需要初始化一个全局唯一的配置类。

Python版本:

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class SingletonConfig(metaclass=Singleton):
    def __init__(self):
        print("SingletonConfig initializing...")


if __name__ == "__main__":
    for i in range(10):
        SingletonConfig()

运行结果:

$ python singleton.py 
SingletonConfig initializing...

更多实现方式见:http://stackoverflow.com/questions/6760685/creating-a-singleton-in-python

Java版本:

// SingletonConfig.java
public class SingletonConfig {
    private static SingletonConfig singletonConfig;

    private SingletonConfig() {};

    public static synchronized SingletonConfig getConfig() {
        if (singletonConfig == null) {
            System.out.println("initializing a new Singleton Config");
            singletonConfig = new SingletonConfig();
        }
        return singletonConfig;
    }
}

// SingletonDemo.java
public class SingletonDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            SingletonConfig config = SingletonConfig.getConfig();
        }
    }
}

运行结果:

$ javac *.java && java SingletonDemo 
initializing a new Singleton Config