装饰模式(Decorator Pattern)

🚏 导论 装饰模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。当现有目标功能不足的时候,需要增强时,可以考虑使用装饰模式。 装饰模式把每个要装饰的代码放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。 装饰器常在面向切面编程(AOP)中使用,比如日志、事务、权限等功能。 🚦 结构 UML类图 classDiagram class Component{ <<interface>> +operation() } class ConcreteComponent{ +operation() } class Decorator{ <<abstract>> -component: Component +operation() } class ConcreteDecoratorA{ -addedState +operation() } class ConcreteDecoratorB{ +operation() -AddedBehavior() } Component <|-- ConcreteComponent Component <|-- Decorator Decorator o-- Component Decorator <|-- ConcreteDecoratorA Decorator <|-- ConcreteDecoratorB Component是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。 装饰模式是利用setComponent来对对象进行包装的。这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。 基本代码 Component类 public interface Component { void operation(); } ConcreteComponent类 public class ConcreteComponent implements Component { @Override public void operation() { System....

January 11, 2025 · 4 min · 751 words · Rex

里氏替换原则(Liskov Substitution Principle)

🚏 导论 里氏替换原则(LSP):子类型必须能够替换掉它们的父类型。 里氏替换原则是Barbara Liskov女士在1988年发表的,A behavioral notion of subtyping, 具体的数学定义如下所示:If S is a declared subtype of T, objects of type S should behave as objects of type T are expected to behave, if they are treated as objects of type T。用大白话翻译就是一个软件实体如果使用的是一个父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化。 由于有里氏替换原则,才使得开放-封闭成为了可能,正是由于子类性的可替换性才使得使用父类类型的模块在无需修改的情况下可以扩展。 🎬 场景 场景一:🐒 动物世界 因为了有了这个原则,使得继承复用成为了可能,只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。猫是继承动物类的,以动物的身份拥有吃、喝、跑、叫等行为,可当某一天,我们需要狗、牛、羊也拥有类似的行为,由于它们都是继承于动物,所以除了更改实例化的地方,程序其他处不需要改变。

January 10, 2025 · 1 min · 47 words · Rex

依赖倒转原则(Dependency Inversion Principle)

🚏 导论 依赖倒转原则 A. 高层模块不应该依赖低层模块。两个都应该依赖抽象。 B. 抽象不应该依赖细节。细节应该依赖抽象。 🎬 场景 场景一:📦 修电脑 当电脑出现蓝屏时,通常考虑是否时内存坏了或是内存条与主板接触不良。如果是内存坏了,那么就需要更换内存条,如果是内存条与主板接触不良,那么就需要重新插拔内存条。得益于电脑部件以及相关接口的设计,只需要了解简单的计算机知识,就可以轻松的定位问题并维修电脑了。他们的设计中体现了依赖倒转原则,当然还有其他的原则(单一职责原则、开放-封闭原则)。依赖倒转原则原话是说抽象不应该依赖细节,细节应该依赖于抽象,其实就是面向接口编程,而不是面向实现编程。无论主板、CPU、内存、硬盘都是在针对接口设计的,如果针对实现来设计,内存就要对应到具体的某个品牌的主板,那就回出现换内存需要把主板也换了的尴尬。 与修📻收音机相比,修电脑反而要简单。是因为收音机耦合过度了,只要收音机出现故障,不管是没有声音、不能调频,还是有杂音,反正都很难修,因为任何问题都可能涉及其他部件,各个部件相互依赖,难易维护。

January 10, 2025 · 1 min · 13 words · Rex

开放-封闭原则(Open Closed Principle)

🚏 导论 开放-封闭原则,是说软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。 对于拓展是开放的(Open for extension),对于更改是封闭的(Closed for modiication) 我们在做任何系统的时候都不要指望系统一开始时需求确定,就再也不会变化,这是不现实也不科学的想法,而既然需求是一定会变化的,那么如何在面对需求的变化时,设计的软件可以相对容易修改,不至于说,新需求一来,就要把整个程序推到重来。怎样的设计才能让软件在需求变化时,可以相对容易的修改呢?这就是开放-封闭原则要解决的问题。 具体来说,在设计的时候,时刻要考虑,尽量让这个类足够好,写好了就不要去修改了,如果新需求来了,增加一些类,原来的代码能不动则不动。当然,绝对的对修改封闭是不可能的。无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。若猜测失败,要及时的去调整。面对新需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。 开放-封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。 🎬 场景 场景一:🏢 公司管理 现要作为老板,给公司制定考勤制度,规定九点上班,不允许迟到。但是有公司骨干,老是迟到。他们也有实际的难处,比如有些人家离公司太远,有些人每天上午要送小孩子上学。需要对他们特殊处理,但不能违反公司的规定。其实迟到不是问题,最主要的是保证8小时的工作量或是完成业绩目标。于是应该改变管理方式,如弹性上班工作制,早到早下班,晚到晚下班,或者每人每月允许三次迟到,迟到者当天下班补时间等等。对市场销售人员可能就更加以业绩为标准,工作时间不固定了。这其实就是对工作时间或业绩成效的修改关闭,而对时间制度拓展的开放。

January 10, 2025 · 1 min · 16 words · Rex

单一职责原则(Single Responsibility Principle)

🚏 导论 单一职责原则(SRP),就一个类而言,应该仅有一个引起它变化的原因。 如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。 软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。 🎬 场景 场景一:📱 手机 现在的手机都有很多功能(除了打电话、发消息):听音乐 、玩游戏、 拍照、摄影等等。但是这里的功能,都是基础功能,实际并没有做的特别专。比如听音乐,手机的音质没有专业的音乐播放器好;拍照,手机的像素没有专业的相机好;玩游戏,手机的操作体验没有专业的游戏机好。所以,手机的功能虽然多,但是都是基础功能,没有做的特别专业。大多时候,一件产品简单一些,职责单一一些,或许是更好的选择。 当然,手机的发展有它的特点,而编程时,我们却是要在类的职责分离上多做思考,做到单一职责,这样代码才是真正的易维护、易扩展、易复用、灵活多样。 场景二:♦️ 俄罗斯方块 俄罗斯方块的实现:俄罗斯方块下落动画的原理是画四个小方块,擦掉,然后再在下一行画四个小方块。不断绘出和擦掉就形成了动画,所以应该要有画和擦方块的动画。然后左右键实现左移和右移,下键实现加速,上键实现旋转,这些其实都应该是函数,当然左右移动需要考虑碰撞的问题,下移需要考虑堆积和消层的问题。 这里大部分都是一些函数,如果将这些函数都放在一个类里,那么这个类就会变得很大,很难维护并且也很难迁移。可以将这些与游戏逻辑相关的函数(下落、旋转、碰撞判断、移动、堆积)提取出来,这些函数和界面如何表示没有特别大的关系,因为所谓方块无非是一个坐标,方块的下落、旋转、碰撞判断、移动、堆积等都是坐标的变化。因此可以将运行界面和游戏逻辑分开,这样就可以更好的维护和迁移。

January 10, 2025 · 1 min · 17 words · Rex

策略模式(Strategy Pattern)

🚏 导论 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。可以用策略模式封装几乎任何类型的规则,只要分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这些变化的可能性。 🧀 前置知识 🚦 结构 UML 类图 classDiagram class Context{ -strategy: Strategy +Context(strategy: Strategy) +contextInterface() } class Strategy{ <<abstract>> +algorithmInterface() } class ConcreteStrategyA{ +algorithmInterface() } class ConcreteStrategyB{ +algorithmInterface() } class ConcreteStrategyC{ +algorithmInterface() } Context o-- Strategy Strategy <|-- ConcreteStrategyA Strategy <|-- ConcreteStrategyB Strategy <|-- ConcreteStrategyC 基本代码 Strategy类,定义所有支持的算法的公共接口。 public abstract class Strategy { /** * 算法方法 */ public abstract void algorithmInterface(); } ConcreteStrategy类,实现具体的算法。 class ConcreteStrategyA extends Strategy { @Override public void algorithmInterface() { System....

January 8, 2025 · 6 min · 1070 words · Rex

抽象工厂模式(Abstract Factory)

🚏 导论 抽象工厂模式(Abstract Factory Pattern),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。当有多个相同的类要实现类似的功能,但是具体的实现细节有所不同,这时候就可以使用抽象工厂模式。 🧀 前置知识 工厂方法模式(Factory Method) 🚦 结构 classDiagram class AbstractFactory{ <<interface>> +createProductA() +createProductB() } class ConcreteFactory1{ +createProductA() +createProductB() } class ConcreteFactory2{ +createProductA() +createProductB() } class AbstractProductA{ <<interface>> } class ProductA1 class ProductA2 class AbstractProductB{ <<interface>> } class ProductB1 class ProductB2 class Client AbstractFactory <|.. ConcreteFactory1 AbstractFactory <|.. ConcreteFactory2 AbstractProductA <|.. ProductA1 AbstractProductA <|.. ProductA2 AbstractProductB <|.. ProductB1 AbstractProductB <|.. ProductB2 Client --> AbstractFactory Client --> AbstractProductA Client --> AbstractProductB AbstractProductA和AbstractProductB是两个抽象产品,之所以抽象,是因为它们有可能有不同的实现,而ProductA1、ProductA2、ProductB1、ProductB2是对两个抽象产品的具体分类实现。...

January 7, 2025 · 6 min · 1260 words · Rex

MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework

基本信息 标题: MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework 作者: Sirui Hong、Mingchen Zhuge、Chenglin Wu(通讯作者)等。 作者单位: DeepWisdom、阿卜杜拉国王科技大学、厦门大学、香港中文大学、南京大学、宾夕法尼亚大学、加州大学伯克利分校、瑞士人工智能实验室 期刊/会议: ICLR 2023 发表年份: 2023 DOI: 2308.00352 开源地址: Github 关键词: MetaGPT, Multi-Agent Collaboration, Standardized Operating Procedures (SOPs), Large Language Models (LLMs), Code Generation 研究背景 (Background) 利用大语言模型的Agent为增强和复制人类的工作流程提供了机会。但是实际应用中,现有系统将复杂问题过度简化。很多人想努力实现有效、连贯和准确的解决问题,尤其是需要协作的任务。而SOP可以有效的分解任务并且协调各个任务,明确的SOPs能够提高任务执行的一致性和准确性,确保其与定义的角色和质量标准相符。 研究问题 (Research Questions) 如何使用应用SOPs与Agent协作开发。 如何优化Agent协作通信能力。 如何提高代码生成的质量。 核心贡献 (Key Contributions) 总结本文的主要贡献点: 引入了MetaGPT,一个基于LLM的多智能体协作元编程框架。 作者在MetaGPT设计中创新性地集成了SOP,减少了LLM的代理之间的无效协作。此外,还引入了一种新颖的执行反馈机制,可以在运行时调试和执行代码,从而提高了代码生成的质量(MBPP上提高了5.4%) 在HumanEval和MBPP达到了SOAT MetaGPT 框架 (MetaGPT Framework) SOP中的Agent(Agents in Stanndard Operating Procedures) 角色的特定职能 解决复杂的任务或问题通常需要具有不同技能和专业知识的智能体协作,每个智能体都针对特定问题提供专门的输出。如在一家软件公司,产品经理的任务是分析业务、软件工程师负责编程开发。因此MetaGPT定义了五个角色:产品经理(Product Manager)、架构师(Architect)、项目经理(Project Manager)、工程师(Engineer)以及QA工程师(QA Engineer)。如下面图1所示: 图1:MetaGPT与真实世界人类团队之间的软件开发SOPs。...

January 7, 2025 · 2 min · 257 words · Rex

Evaluating Large Language Models Trained on Code

基本信息 标题: Evaluating Large Language Models Trained on Code 作者: Mark Chen、Jerry Tworek、Heewoo Jun、Qiming Yuan等 作者单位: OpenAI, Anthropic AI, Zipline 期刊/会议: arXiv 发表时间: 2021年7月14日 DOI: 2107.03374 开源地址: GitHub 关键词: 代码生成, 代码评估, 代码理解 研究背景(Background) 对于代码生成任务由于语言模型的推动最早的研究在1963、1971年就开始了(Experiments with a heuristic compiler、Toward automatic program synthesis),最近的有基于掩码语言模型(masked language modeling)和跨度预测(span prediction)改编用于训练其编程对应物的有CodeBert和PyMT5。OpenAI在GPT3发布的时候,就已经可以通过文档注释(docstrings)生成python代码了,但是能力还是比较有限。 研究目标(Research Objectives) 提出一种评估大型语言模型生成代码能力的方法,包括pass@k指标和HumanEval数据集。 提出Codex,通过对GPT-3进行微调。 提出Codex-S,通过有监督微调Codex。 提出Codex-D,通过训练docstring生成模型。 评估框架 (Evaluation Framework) 作者定义了pass@k指标,以及HumanEval数据集,用于评估Codex的性能。 功能的正确性(Functional Correctness) 评估生成质量可以通过匹配的方式:精确匹配或模糊匹配(如BLEU得分)。然而有研究揭示了基于匹配的代码评估指标的不足。如论文CodeBLEU: a Method for Automatic Evaluation of Code Synthesis中发现BLEU在捕捉代码特有的语义特征方面存在问题,并建议对分数进行几个语义的修改。更加根本的问题是,基于匹配的方式无法评估庞大的并且复杂的代码。近期无监督的代码翻译或伪代码到代码的翻译相关问题也转向了功能性准确性的评估,如果样本通过了测试用例,那么就认为生成的代码是正确的。作者认为这种方式应用到代码生成的评估中是合理的。评估功能正确性最有说服力的理由是程序员常使用这种方式进行评估(单元测试)。 Kual等人在2019(SPoC: Search-based Pseudocode to Code)就用了pass@k,对于每个问题生成k个代码样本,如果其中有一个样本通过了单元测试就认为问题已得到了解决,并计算解决问题的总比例。然而作者认为以这种方式计算pass@k会有很高的方差,而且计算量大。相反,为了评估pass@k,我们给每个任务生成n个样本(n > k, 论文中使用n = 200, k = 100), 统计通过单元测试的正确样本数c(c <= n),并计算无偏估计量。其中 $\text{pass@}k := \mathbb{E}_{\text{Problems}} \left[ 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}} \right]$。...

January 1, 2025 · 2 min · 404 words · Rex