linux内核中的设计模式

2023-11-19

 
 
创建型
 
 
Object Pool
 
Object Pool模式可以提升性能,尤其是在对象的分配、初始化成本高,使用频率高
但使用时间短的情况下。对象池可以设置对象池的大小和回收时间缓存预分配的对象。
NT和Linux都有简单的预分配缓存对象的机制,也就是Lookaside Cache机制。NT的
对象管理器使用延迟删除和垃圾回收机制实现真正意义上的对象池。在调用NtClose
关闭对象之后,对象使用的内存空间并不马上回收,而是挂在已删除对象列表之中,
并且被设置为删除。在新生成对象时,对象管理器先去查找已删除的同类对象。内核
定时启动垃圾回收工作线程回收过多的已删除对象。
 
Prototype
 
Prototype模式可设定类型的对象创建实例,通过拷贝这些原型创建新对象。例子如下:
class Make {
public:
    Make(Object1 *o1, Object2 *o2...);
    Object1 *make_c1(void) {c1->clone()}
    Object2 *make_c2(void) {c2->clone()}
    ......
};
Linux中有一个很典型的应用Prototype模式的系统调用——clone。它体现了Linux Kernel
开发者对进程、线程以及共享资源的理解。clone中flags参数可以指使clone所需的资源,
可以是文件系统信息或I/O环境信息或IPC资源或网络信息或地址空间,而是否clone地址
空间决定了系统会产生一个新的线程还是一个新的进程。
更有趣的是,操作系统还会提供浅拷贝的功能。NT的系统调用NtDuplicateObject可以在
不同的进程空间中复制内核对象的引用。常见的场景是使本进程也可以访问其它进程打开
系统资源。Linux提供了dup和dup2两个系统调用,它们可以实现进程内文件描述符复制,
dup2则主要用于抢占标准输入或标准输出描述符位置。Linux还存在着一种自动的浅拷贝
机制——Unix域。它可以在发送和接收两者之间传递多个文件描述符,并自动为接受进程
建立可用的文件描述符,提高了共享文件的效率。
 
Singleton
 
Singleton模式提供访问类的全局实例并且只有一个,它封装了类的初始化时间和方式。
这是一个简单又复杂的模式。习惯面向过程的程序员常常将它用来代替全局变量,以规避
团队编程规范中不许使用全局变量的条款。
 
在内核开发中,开发者通常是熟悉C语言的程序员,他们并不避讳直接使用全局变量。原因
是参与开发的程序员大多经验丰富,并有相当的程序设计能力。但从为应用程序提供服务
的角度看,内核有不得不设计系统调用读或者写它内部的某些全局变量。比如Windows的:
BOOL SystemParametersInfo(UINT Action, UINT Param, PVOID Param, UINT WinIni);
Action包括了10个不同的种类,每个种类的Param都有相应的全局的内核数据,两者组合
起来大概可以表示上百项不同的内容。
 
NT中存在一个更加奇特的函数,它未被微软公开却拥有着比官方文档更丰富的说明描述。
尽管MSDN中强调该API有可能在未来修改,但真有人相信吗?NtQuerySystemInfomation,
一个由5000+行代码实现的函数。这个霸气的函数说明了一个简单的道理,“清规戒律”
只适用于能力不足的人。通过C struct传递内核数据并不是一种讨巧的方法,而Windows
开发者似乎也没有更好的办法,他们在struct的第一项中放置cbSize,期望在以后的变化
里,Size就代表Version。Linux就聪明多了,如果Linux就是CUI的天下,那么为什么不
直接给它们字符串呢?通过特殊的文件系统,用简单的文件函数就读写内核数据,连格式
转换都省了。其实NT中也有类似的机制,特殊的树形结构除了文件系统之外,还可以是
NT特有的registry。注册表中的一些特殊键直接映射到内核变量上,因此通过注册表API
便可可访问内核变量,只不过这种机制常常由系统自带的软件使用而很少有人知道罢了。
不管怎么说,无论是结构体还是字符串都无法避免对数据版本的理解,形式反而是次要的。
那么C++中Singleton到底如何呢?
class Singleton {
public:
    static Singleton *instance(void) {
        static Singleton *_instance = 0;
        if (!_instance)
            for (_instance = singleton();
                 _instance && strcmp(name, s->name);
                 _instance = _instance->next);
        return _instance;
    }
protected:
    Singleton(const char *name) : name(name), next(0) { assert(this->name); }
    static Singleton *singleton(void) {
        static Singleton Singleton("default");
        return &Singleton;
    }
    static void RegisterSingleton(Singleton *singleton) {
        Singleton *s = Singleton::singleton();
        while (s->next)
            s = s->next;
        s->next = singleton;
    }
private:
    const char *name;
    Singleton *next;
};
class Singleton1 : public Singleton {
public:
    Singleton1(void) : Singleton("1") {
        RegisterSingleton(this);
    }
};
static Singleton1 Singleton1;
 
最开始的版本打算用std::map组织name和singleton,但最后还是基于简单的链表实现。
这个例子包括了实现Singleton模式的各种技巧,本例的设计以自然为原则,工程中使用的
代码或多或少会根据不同的原则有所修改。在第一次调用Singleton::instance()时初始化
实例可以避免集中初始化大量的Singleton引起的性能问题,又可以防止过早初始化带来的
初始化依赖问题。但如果必须尽早初始化,那么还需要再添加一个触发器:
class Singleton {
    struct Creator {
        ObjectCreator(void) {
            Singleton::instance();
        }
    };
    static Creator creator;                          
};
 
Builder
 
Builder模式将复杂的对象分解几个不同的部分,不同的Builder可以对不同部分按照不同
方式生成结果:
class Builder {
public:
    virtual void makePart1(Element *) = 0;
    virtual void makePart2(Element *) = 0;
    virtual void makePart3(Element *) = 0;
};
Maker::make(stream)
{
    while (element = stream->next()) {
        switch (element.type()) {
        case part1:
            builder->makePart1(element);
            break;
        case part2:
            builder->makePart2(element);
            break;
        case part3:
            builder->makePart3(element);
            break;
        default:
        }
    }
}
maker.setBuilder(&Builder1);
maker.make(stream);
 
内核内部的创建型模式很少见,因为内核模块结构简单,既没有面向对象语言提供的多态
机制也不需要想办法替换new操作符。而系统调用接口常常被设计成功能明确单一,具有
良好的正交性。然而NT中却存在特例:
NTSTATUS NtCreateSection(PHANDLE SectionHandle, ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
    PLARGE_INTEGER MaximumSize OPTIONAL, ULONG SectionPageProtection,
    ULONG AllocationAttributes, HANDLE FileHandle OPTIONAL);
内核是按照应用程序提供的文件句柄创建Section内核对象的。最后的FileHandle应该是
NtCreateFile生成的句柄,按照MSDN上的描述这里的File可以是:Files, File Streams,
Directories, Changer Device, Tape Drives, Communications Resources, Consoles,
Mailslots Pipes, 笔者再补充一个socket。其实就是内核中所有存在的文件系统驱动和
设备驱动所创建的文件或设备。
 
 
结构型
 
 
Facade
 
Facade模式为复杂的系统封装一个简单的接口,并且可以单独访问子系统。操作系统的
系统调用是一种相对简单的API,它们封装了操作系统内部若干子系统与子系统之间的
复杂细节。同时操作系统也会提供加载内核模块的机制用以扩展内核结构,而且将内核中
的部分代码以内核模块的形式加载可以减少编译内核的时间,提高开发者的工作效率。
 
Proxy
 
Proxy模式可以控制对象的访问权限,改变对象的访问方式,使对象和对象的访问方式
相分离。在C++中,基本的对象方式是指针或引用,而指针或引用都不那么“智能”,
标准库提供了基于代理模式的智能指针弥补这些不足,如C++98引入而在C++11中废弃的
auto_ptr,C++11引入shared_ptr和unique_ptr(替代auto_ptr)。操作系统并不信任
应用层的程序,它为应用程序提供了间接的内核对象访问机制。应用程序并不能直接获得
内核对象指针,在Linux中提供的是fd而在NT中则是handle。内核负责根据相应的Key找到
对应的内核对象,并继续完成该系统调用功能。特别的,NT内核使用的handle还能表示
更多语义,handle的最后2位保留给应用程序使用,而高位表明该handle是系统全局句柄
或是当前进程的句柄。
 
Adapter
 
Adapter模式将一类的接口转换为符合期望的另一种接口,使旧组件匹配新的系统接口。
可能是下面这种形式:
class Adapter: public Target, private Legacy {
};
 
或者:
class Adapter: public Target {
public: Adapter(Adaptee *);
};
 
NT在设计之初的目标便是超越Unix,兼容POSIX、OS/2、Windows等不同的子系统。NT并不
在系统调用接口基础上直接兼容各种子系统,而是借助各种Adapter动态链接库适配不同的
接口。在一种系统上兼容另一种系统通常还会需要一个该子系统的管理进程,用于维护该
子系统的公共数据,例如管理Windows子系统的csrss进程。而其它子系统在操作系统发展
过程中被逐步削弱不见了踪影。POSIX接口的实现被放在了C Runtime Library中,Win32
子系统提供的功能足够丰富以支撑POSIX,然而由于设计理念不同,兼容接口在细节上仍然
稍有不同。除此之外,socket与winsock、OpenGL与DirectX等都是用NT原生API以Adapter
的方式兼容其他不同标准的接口。
另一个相反的例子是Wine,一个在Linux平台上兼容Windows的开源项目。Wine有20年的发展
历史,并涉及到了很多的内核设计技术,然而更难的是兼容一个没有足够文档和实现参考的
系统。
 
N.B. Wine包含了一系列类似Windows的Adapter动态链接库和WineServer,为了兼容多种
Unix系统,它本身并不包含内核模块,但一个代号龙井的Wine衍生项目却单独为Linux
开发了Wine的内核模块。
 
N.B. 另一个号称兼容Android的项目也是使用Adapter模式?不知道,但闭源的Adapter
兼容开源的Adaptee不都是可耻的吗?
 
Bridge
 
Bridge模式的目标是从设计中抽象出其实现,使两者可以独自改变。这简直就是禅语,只要
是谈及设计模式的资料基本都会提到这句话。但Bridge模式的原则其实很简单,就是别在
滥用继承了,并列举了一个通过继承机制实现的糟糕设计。一个窗口系统需要兼容Windows,
Linux, MacOS三种平台,而窗口本身又有toplevel, child, popup三种。如果用继承方式
设计,那么最后我们将得到3 * 3种类。如果系统中扩充了dialog, button, label, date,
editor,    combobox之后,数量将会是3 * 6。但如果用组合方式设计只需要实现3 + 6个类。
Bridge将m * n降低到与m + n。
内核需要兼容各种体系结构,然而每种体系结构的硬件细节并不相同,内核是如果做到的?
这个问题首先和软件的分发方式有关,开源的Linux可以提供相应的编译参数,让用户自己
根据目标机器选择编译参数,而闭源NT就不得不通过安装程序检测相关硬件信息了,安装
程序自带所有支持的硬件体系相关的动态链接库,并根据刺探硬件信息选择对应的文件,
最后重名为hal.dll供启动内核使用。
大部分内核模块都是与硬件无关的,少数功能需要硬件提供的机制支持,例如内存管理、
线程调度、中断管理、同步机制等。从全局的角度看,Linux的调度管理确实是一种Bridge
模式,此类代码是如此少见而又难得。我们知道,Linux按照POSIX标准的要求实现了5种
调度策略:(SCHED_NORMAL, SCHED_BATCH, SCHED_IDLE), (SCHED_RR, SCHED_FIFO)。其中
前三种是由公平调度类(sched_fair)实现的,后两种是由实时调度类(sched_rt)实现的。
调度类结构定义了调度类的接口:
struct sched_class {
    next                : 下一个调度类指针
    enqueue_task()      : task入队
    dequeue_task()      : task出队
    yield_task()        : task让出cpu
    pick_next_task()    : 选择下一个被调度task
};
在真正进行调度时,调度管理器会调用context_switch函数,硬件相关的操作都封装在它
里面,主要有:switch_to和entry_lazy_tlb。前者负责跳转到新线程上下文,后者冲刷
CPU中MMU的相关TLB。每种硬件体系模块都提供了这两个函数,并在编译时已经建立好正确
的链接,这种特殊的Bridge竟然有编译器的参与。
 
Composite
 
Composite模式描述了一种用树形结构组织对象的方式,它允许单一访问对象或递归访问
多个对象。C++11(gcc 4.6.0)代码如下:
class Component {
public:
    virtual void do(void) = 0;
};
class Leaf : public Componet {
public:
    void do (void) {}
};
class Composite : public Component {
public:
    void do(void) {
        for (auto index : children)
            index->do();
    }
    void add(Component *component) {
        children.push_back(componet);
    }
private:
    std::list<Component *> children;
};
 
文件系统是一种更加抽象的Composite模式。目录被看做是特殊的文件,也就是Composite的
add可基于Component接口实现。无论是NT的NtQueryDirectoryFile或者Linux的getdents,
它们都将目录的查询当成是目录文件的一次读过程。如果当前目录的文件足够过而调用者
提供的缓冲区不足,而调用者若要遍历整个目录必然要进行多次调用,每次的读取位置便是
由文件seek操作的文件偏移记录的。
内核同样善于用树形结构组织各种内核对象,例如NT存在的对象管理子系统。Linux用结构
kobject和对应的辅助函数管理数据:
struct kobject {
    struct list_head entry;
    struct kobject *parent;
    struct kset *kset;
    struct kobj_type *ktype;
    struct sysfs_dirent *sd;
    struct kref kref;
};
kref用于访问计数,list_head用于连接同类对象,parent指向父亲,sd代表目录结构
并包含了孩子对象。ktype中包括了针对kobject的各种操作。
 
Decorator
 
Decorator模式用于动态组合相关对象依次完成一系列操作。C++代码如下:
class Decorator {
public:
    explicit Decorator(Decorator *d = 0) : d(d) {}
    virtual ~Decorator(void);
    virtual void do(void) {
        d->do();
    }
private:
    Decorator *d;
};
class Decorator1 : public Decorator {
public:
    explicit Decorator1(Decorator *d = 0) : Decorator(d) {}
    virtual ~Decorator1(void);
    void do(void) {
        Decorator::do();
    }
};
class Decorator2 : public Decorator {
public:
    explicit Decorator2(Decorator *d = 0) : Decorator(d) {}
    virtual ~Decorator2(void);
    void do(void) {
        Decorator::do();
    }
};
Decoratoor *d = new Decorator1(new Decorator2());
d->do();
 
NT的设备驱动管理使用了类似的机制管理各种硬件的驱动程序。NT I/O内核子系统动态生成
设备栈,以文件访问为例:
1.应用程序发起文件I/O请求。
2.内核查找卷设备,将请求发送给卷设备对应的文件系统驱动。
3.文件系统驱动有上层过滤驱动和下层过滤驱动和功能驱动(也就是它自己)。
4.如果没有缓存,文件系统驱动将请求发送到下面的磁盘设备驱动。
5.磁盘设备驱动同样有上层过滤驱动和下层过滤以及功能驱动三部分,请求换为SCSI形式。
6.接下来请求被发送到磁盘驱动器设备驱动,它访问真正的硬件设备,读写相应数据。
 
NT希望这个过程是可以动态配置的,上层驱动只需要将请求发送给下层驱动,但并不需要
了解下层驱动。这些配置信息保存在注册表:HKLM\SYSTEM\CurrentControlSet\Enum中。
而驱动程序只需要在调用I/O子系统提供的内核函数即可向下传递请求:
NTSTATUS IoCallDriver(PDEVICE_OBJECT DeviceObject, PIRP Irp);
 
Private Class Data
 
Private Class Data模式常用来解决面向对象语言的语法带来的工程问题。C++类的声明
通常会放在头文件中,这会使:
 
1.每次修改私有方法或变量都可能引起大范围的编译。
2.const变量必须在类初始化时便要决定值。
3.类成员变量名和类方法名冲、方法参数名冲突。
4.不同的成员变量不能分组区分访问。
5.暴露类的私有成员和数据结构。
好吧,其实也没有那么糟糕,除了1。不过,只要修改头文件之后尽快启动编译器就可以了。
 
因此,应用该模式的类应该这样设计:
class ClassPrivate;
class Class {
private:
    ClassPrivate *data;
};
 
在操作系统中这种模式却另有妙用:
当nIndex为GWLP_USERDATA时,它返回的是通过相应的Set函数设置的自定义指针。
ATL库巧妙的使用过私有指针。与MFC或Qt库不同的是,ATL库将hWnd与ATL类的对应关系
放在该私有指针中,利用系统提供的hWnd查找窗口对象的机制查找hWnd与类的关系。其它
图形库会自行建立索引关系,但降低了性能却又增加软件复杂度。
 
Linux fs.h: struct file {
/* needed for tty driver, and maybe others */
void *private_data;
};
本来为tty驱动提供的私有指针却在实现epoll机制用来,
struct eventpoll *ep = file->private_data;
 
私有数据不但可以用组织内部程序,还可以用于建立该对象与其它对象之间的联系。
 
 
行为型
 
 
Null Object
 
Null Object模式为系统提供了一个默认的行为,也就是什么都不做。Null Object模式
往往提供了一种语义,增强了系统的概念完整性。例如Linux系统中的/dev/null设备,
它在原有体系中补充了一个特例,在不违反原有原则的基础上实现了一类特殊的功能。
NT中也有一个类似的例子:Raw File System Driver。如果NT系统中的文件系统驱动都
未能成功地挂载到某个磁盘设备上时,NT会将Raw挂载到该磁盘设备。Raw驱动会将读写
请求发送到它下层的磁盘设备驱动,最大程度上减少卷设备挂载失败对系统的影响。
Null Object提高了系统的容错能力并补充了一类特例,既有合理性又具有特殊性。
 
Strategy
 
Strategy模式是通过定义一系列接口将实现细节封装起来,使得它们能在不同的情况下使用
不同的实现。Strategy模式在内核中非常常见,下面是C++代码:
class Context {
public:
    void LookupStrategy(int type) {
        if (type == 1)
            current = new Strategy1();
        else if (type == 2)
            current = new Strategy2();
        else if (type == 3)
            current = new Strategy3();
    }
    void do(void) {
        current->ops();
    }
private:
    Strategy *current;
};
class Strategy {
public:
    void ops(void) {
        strategy->do();
    }
private:
    virtual void do(void) = 0;
};
class StrategyN : public Strategy {
private:
    void do(void) {}
};
 
由于用户态程序不能直接访问驱动程序,那么内核必须在两者之间建立桥梁。比如VFS,
它在Linux中相当于Context和Stragtegy中的共同方法两部分,而各种文件系统驱动就是
不同的Strategy。应用程序访问不同的路径时,VFS会找到相应的文件系统驱动并建立文件
描述符与Stragte的关系。
更重要的是在内核中Context可以动态匹配应用程序的请求和当前已经注册的模块。匹配的
方式可以是某种路径,也可以直接将原始数据发送给已经注册的模块对其进行识别。
在Linux中mount, load executable file, lookup file path都会触发这种自动匹配过程,
它们有相似的结构和类似的工作方式。
 
N.B. 值得一提的是内核中的网络结构,NT的NDIS网络子系统和Linux的if/proto管理,它们
采用了Strategy和Decorator结合模式。网络子系统会将协议驱动和数据链路驱动分别与
接收到的数据包匹配,并将处理过的数据向后发送。
 
Template Method
 
Template Method模式定义一个操作中的算法的骨架,将一些步骤延迟到子类,这样子类
重新定义算法的某些步骤不改变算法的结构。
class Template {
public:
    void do(void) {
        step1();
        step2();
        step3();
    }
private:
    virtual void step1(void) = 0;
    virtual void step2(void) = 0;
    virtual void step3(void) = 0;
};
class BaseMethod {
private:
    void step1(void) {}
    void step2(void) {}
    void step3(void) {}
};
class Method1 : public BaseMethod {
private:
    void step1(void) {}
};
class Method2 : public BaseMethod {
private:
    void step2(void) {}
};
class Methon3 : public BaseMethod {
    void step3(void) {}
};
 
内核没有提供过可以替换某一过程中核心功能模块的机制,但可以在一些关键点上嵌入
一些过滤器。这种工作方式只能部分体现Template Method模式的功能,但这已经可以
满足内核设计中的相关需求。例如,Linux中的netfilter过滤机制,NT中的Upper Filter
Driver和Lower Filter Driver(结构模式而非行为模式)以及Windows的SetWindowsHookEx。
这些机制不仅可以工作在关键步骤上,还可以形成一条Decorator处理链。
 
N.B. 某些工作在内核空间的工具可能会替换掉一些结构体的函数指针,形成多态子类化
的效果,当然它们往往为了不影响正常的内核功能最后还会调用原来的函数指针。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux内核中的设计模式 的相关文章

  • c语言实现面向对象编程(const * ,* const)

    c语言实现面向对象编程 面向对象思想 封装 继承 多态 代码实现 函数签名及返回值的约定 const 重载 参考了onlyshi的博客代码 orz传送门 参考了嵌入式实践一些代码 这里就不加了 面向对象思想 面向对象编程 OOP 并不是一种
  • 行为型模式-策略模式

    package per mjn pattern strategy 抽象策略类 public interface Strategy void show package per mjn pattern strategy 具体策略类 用来封装算法
  • 设计模式学习笔记(一)之单例模式

    单例模式 作用 保证一个类只有一个实例 并且提供访问这个实例的全局访问点 应用场景有 数据库连接池 spring中 Bean默认是单例 Servlet中 每一个Servlet是单例 配置文件的类 一般是单例 优点 单例只生成一个实例 减少系
  • C++设计模式(二)观察者模式

    1 观察者模式知识点 1 定义 定义对象间的一种一对多的依赖关系 当一个对象的状态发生改变的时候 所有依赖它的对象都得到通知并自动更新 2 动机 将一个系统分割成一系列相互协作的类有一个常见的副作用 需要维护相关对象间的一致性 我们不希望为
  • 设计模式——原型模式

    原型模式顾名思义 就是指以某个实例为原型 copy出一个新的实例 该实例属性与原型相同或者是类似 很多时候 我们需要创建大量的相同或者相似的对象 如果一个个用new 构造函数的形式去创建的话比较繁琐 就像孙悟空要想变出成千上万个猴子猴孙总不
  • java需会(转载)

    一 基础篇 1 1 Java基础 面向对象的特征 继承 封装和多态 final finally finalize 的区别 Exception Error 运行时异常与一般异常有何异同 请写出5种常见到的runtime exception i
  • [C++]备忘录模式

    备忘录模式 Memento Pattern 保存一个对象的某个状态 以便在适当的时候恢复对象 备忘录模式属于行为型模式 github源码路径 https github com dangwei 90 Design Mode 此文件包含 mai
  • 分享几个项目中用到的设计模式

    前言 之前项目中出于扩展性和有雅性的考虑 使用了多种设计模式进行项目框架的设计 主要的一些设计模式是单例模式 工厂模式 策略模式 责任链模式 代理模式这几种 现在依次讲讲这几个的主要是实现方式和在我们项目中的应用场景 核心设计模式分享 单例
  • 每日一问:你想如何破坏单例模式?

    前言 1 单例是什么 单例模式 是一种创建型设计模式 目的是保证全局一个类只有一个实例对象 分为懒汉式和饿汉式 所谓懒汉式 类似于懒加载 需要的时候才会触发初始化实例对象 而饿汉式正好相反 项目启动 类加载的时候 就会创建初始化单例对象 1
  • HeadFirst 设计模式学习笔记10——MVC分析

    1 M V C Model View Controller 模式 视图 控制器 这是一种范型 模型对象正是应用系统存在的理由 你设计的对象 包含了数据 逻辑和其他在你的应用领域创建定制的类 视图通常是控件 用来显示和编辑 控制器位于二者中间
  • 程序员必知的23种设计模式之组合模式

    文章目录 1 模式引出 学校院系展示需求 1 1 传统方案 1 2 传统方案问题分析 2 组合模式基本介绍 2 1 方案修改 3 组合模式解决的问题 4 组合模式的注意事项和细节 1 模式引出 学校院系展示需求 编写程序展示一个学校院系结构
  • 设计模式——State(状态)模式

    目录 前言 1 定义 2 适用性 3 结构 3 1 结构图 3 2 参与者 4 应用举例 4 1 State TcpState 4 2 Context TcpConnection 4 3 ConcreteState ListeningTcp
  • 泛型与反射机制在JDBC和Servlet编程中的实践

    写在前面 泛型与反射是java中的两种强大机制 可以很好的提高代码的灵活性和复用性 本篇文章向大家展现在JDBC和Servlet编程场景下反射和泛型技术的实践 通过灵活使用这两种机制打造 高度可复用的JDBC和Servlet代码 1 JDB
  • Java监听器与观察者模式

    Java监听器与观察者模式 Java中的监听器 Listener 和观察者模式 Observer Pattern 都是用于处理对象间的事件通知和响应的设计模式 它们的目的是在对象之间建立一种松散的耦合 使得一个对象的状态变化可以通知到其他对
  • 设计模式—迭代器模式解析

    本文参考学习了 图解设计模式 中的代码实现和原理解释 迭代器模式 简介 Iterator 模式用于在数据集合中按照顺序遍历集合 就类似于我们的循环 一个个去遍历一个集合中的所有元素 示例代码 首先我们思考一个书本和书架的关系 显然 书架可以
  • 设计模式(三)-结构型模式(4)-组合模式

    一 为何需要组合模式 Composite 在代码设计中 有种情况是对象之间存在层次关系 即对象之间会存在父结点和子结点的关系 比如在文件管理系统中 所有文件和文件夹形成树状结构 文件夹目录里存在子文件夹和文件 文件夹属于枝结点 文件属于叶结
  • C++设计模式 #3策略模式(Strategy Method)

    动机 在软件构建过程中 某些对象使用的的算法可能多种多样 经常改变 如果将这些算法都写在类中 会使得类变得异常复杂 而且有时候支持不频繁使用的算法也是性能负担 如何在运行时根据需求透明地更改对象的算法 将算法和对象本身解耦 从而避免上述问题
  • 【设计模式之美】理论一:怎么才算是单一原则、如何取舍单一原则

    文章目录 一 如何判断类的职责是否足够单一 二 类的职责是否设计得越单一越好 开始学习一些经典的设计原则 其中包括 SOLID KISS YAGNI DRY LOD 等 本文主要学习单一职责原则的相关内容 单一职责原则的定义 一个类只负责完
  • 【设计模式之美】面向对象分析方法论与实现(二):需求到接口实现的方法论

    文章目录 一 进行面向对象设计 1 划分职责 gt 需要有哪些类 2 定义类及其属性和方法 3 定义类与类之间的交互关系 4 将类组装起来并提供执行入口 二 如何进行面向对象编程 1 接口实现
  • 系列一、 单例设计模式

    一 单例设计模式 1 1 概述 单例模式 Singleton Pattern 是Java中最简单的设计模式之一 这种类型的设计模式属于创建者模式 它提供了一种创建对象的最佳方式 这种模式涉及到一个单一的类 该类负责创建自己的对象 同时确保只

随机推荐

  • 为什么List,set,map 不继承Serializable接口

    为什么List set map 不继承Serializable接口 猜测 应该是默认不继承 但实际上可以继承 只要是object都可以实现这个接口只是默认不这样干 有三个可能 一 是不知道怎么实现默认接口 二 不允许实现默认接口 三 暂时没
  • UITextFeild Test

    import
  • selenium的使用

    selenium的使用 0 使用selenium import time from selenium webdriver import Chrome from selenium webdriver common by import By 1
  • mos管的rc吸收电路计算_RCD吸收电路

    一 首先对MOS管的VD进行分段 输入的直流电压VDC 次级反射初级的VOR 主MOS管VD余量VDS RCD吸收有效电压VRCD1 二 对于以上主MOS管VD的几部分进行计算 输入的直流电压VDC 在计算VDC时 是依最高输入电压值为准
  • 大型网站架构不得不考虑的10个问题

    这里的大型网站架构只包括高互动性高交互性的数据型大型网站 基于大家众所周知的原因 我们就不谈新闻类和一些依靠HTML静态化就可以实现的架构了 我们以高负载高数据交换高数据流动性的网站为例 比如海内 开心网等类似的web2 0系列架构 我们这
  • Visual Studio Code 的安装教程和配置C语言环境(详解版)

    最近想装一个VS Code 来写C C 程序 但是看了网上的很多教程发现并不是那么的好 大部分都尝试失败了 摸索了很久找到了一个比较可靠的方法 目录 一 Visual Studio Code 的安装教程 二 接下来就是C语言的环境配置 三
  • (OD)基站维护工程师

    目录 题目描述 输入描述 输出描述 代码 题目描述 小王是一名基站维护工程师 负责某区域的基站维护 某地方有n 个基站 1
  • Rust swap

    文章目录 fn swap lt a gt a a mut String b a mut String let tmp a a b b tmp let mut a aaa to string let mut b bbb to string s
  • 浅解cocos2d-x中的CCSprite绘制原理

    cocos2d x版本为2 0 4 此画图调用的是opengl es 2 0版本 支持三角形画图 故必须有一个顶点数组 此定义定义在CCSprite h中 ccV3F C4B T2F Quad m sQuad 而这个顶点数组的定义为 4 c
  • Altium designer Silkscreen Over Component Pads

    在画pcb的时候 执行设计规则检查的时候总会出现Silkscreen Over Component Pads这个问题 该问题的意思是丝印层的文字和元件焊盘重合或者挨着很近 解决办法1 修改规则 在design rule中选择Silkscre
  • django解决使用DateTimeField添加、修改记录时不动态更新时间的问题

    解决方法 定义model时 若想动态显示最后的修改时间 使用 from django db import models from datetime import datetime models DateTimeField default d
  • 码云协同开发

    一 协同开发 为每一开发者创建一个分支 各自都在各自的分支上写代码 互不影响 完成后再合并dev分支 方式1 创建项目合作者 方式2 创建项目合作者
  • IP组播 —— IP组播的概念和地址

    一 IP数据报的三种传输方式 二 IP组播地址的范围及特点 三 硬件组播
  • SpringBoot找不到主类

    用idea把一个单独的springboot项目打开可以正确执行 可我把整个运维项目都放在一个目录用idea打开 idea识别不到主类 查看edit configurations里面的 main class也指定到了对应的applicatio
  • C#初始化数组的三种方式

    C 声明数组并初始化 有三种方式 对于一维数组 using System using System Data using System Configuration using System Web using System Web Secu
  • jenkins创建html文件夹失败,jenkins html发布者:目录存在,但未能复制到

    难道有人有不同的答案吗 Jenkins安装在Ubuntu 12 04下的tomcat下 我已经配置了使用CVS存储库进行构建 当我尝试进行新构建时 由于以下错误而失败 INFO INFO BUILD SUCCESS INFO INFO To
  • [实习]git ci/cd概念,创建流程以及常见字段含义

    1 基本概念 1 1 CI CD CI Continuous Integration 为持续集成 即在代码构建过程中持续地进行代码的集成 构建 以及自动化测试等 有了 CI 工具 我们可以在代码提交的过程中通过单元测试等尽早地发现引入的错误
  • 【安全脚本】模拟勒索病毒

    0x00 前置 1 将电脑上的重要文件加密 将文件以二进制的方式进行加密处理 导致加密过后的文件 要打开必须要解密 要解密必须要解密程序 2 传播 系统or程序漏洞 人为疏忽 后门或木马程序 3 解决 交钱 破解 数据备份 0x01 pyt
  • 华为机试题103-Redraiment的走法

    描述 Redraiment是走梅花桩的高手 Redraiment可以选择任意一个起点 从前到后 但只能从低处往高处的桩子走 他希望走的步数最多 你能替Redraiment研究他最多走的步数吗 数据范围 每组数据长度满足1 n 200 数据大
  • linux内核中的设计模式

    创建型 Object Pool Object Pool模式可以提升性能 尤其是在对象的分配 初始化成本高 使用频率高 但使用时间短的情况下 对象池可以设置对象池的大小和回收时间缓存预分配的对象 NT和Linux都有简单的预分配缓存对象的机制