前语

okey,最近在写个小结构,那么不免会用到规划方式相关的内容,当然还有个原因便是要准备软考。所以的话咱们今天的话把咱们的规划方式从头过一遍,当然这儿留意的是,由于每个人的了解是不同的话,因此关于涉及方式来说,咱们的案例代码或许也是不太相同的。所以假如你觉的我这儿的某些描绘有点问题,请在评论区咱们一同评论。那么在这儿的话,也是会有一些古怪的比喻,重在了解。

规划准则

规划准则是能够了解是咱们的一种代码编写的建议标准。咱们接下来的规划方式根本上都是同这几个规划准则相关的,换句话说,咱们的规划方式能够协助咱们的代码在必定程度上符合标准咱们的代码满足规划准则。那么为什么咱们要学习运用规划方式呢?

这个问题其实就好像咱们为什么在学习高等数学的时分,或许初等(高中)数学的时分,为什么咱们要学习二级定论,要学习技巧的道理是相同的。假如有学习过考研数学的话,我想你关于这些话形象会很深入。许多固定的标题,假如在第一步你没有想清楚(思考出大致的解题链路)你将很苦楚(当然现在不仅仅是思路的问题,还有核算能力的问题)关于咱们的代码相同如此

那么在这儿咱们一共有六个根本的规划准则 六大根本规划准则

单一职责准则

单一职责准则是一个基础的规划准则。其中心是确保,咱们写的每一类只做一个件工作。防止天主类的呈现,导致代码臃肿不利于对扩展敞开。

以咱们人类社会为比方:单一职责告知咱们每个人有属于,每个人的人物

开闭准则

依据接口,来进行声明,合作单一职责准则,完成对代码的扩展敞开。在需求新功用的时分,只需求完成新的接口,然后经过组织方式来将其进行替换。

开闭准则告知咱们,从事核算机软件开发时,咱们能够在同一行业界张狂跳槽

里氏替换准则

承继必须具有超类的一切特性。(对类进行分类,完成不同的功用)

举个比方:龙生龙,凤生凤,老鼠的儿子会打洞。一切的子类承继父类,都需求确保具有父类的一切的功用,之后再新增新的功用。

迪米特法则

下降类之间的耦合,不同人物,做不同的事。仅仅向上开发的时分,供给接口,而供给完成。下级只需求为上级供给接口,而不能供给详细完成,详细完成能够由同级完成,让上级调用。

接口阻隔准则

单一职责是对类来说,防止天主的呈现,接口阻隔准则则是对接口来说,来防止天主接口的呈现。这个和里氏替换准则是息息相关的

依靠倒置准则

高层不能依靠底层。尖端接口完成,能够依靠底层接口完成,可是底层不能直接依靠高层,同级可依靠。

口诀 单一职责:尽或许自己做自己的事 开闭准则:多加少改,后面再合作工厂方式,战略方式等等。 里氏替换准则:咱们需求承继和发扬我国传统文化(子类必须具有超类的一切完成) 迪米特法则:尽或许供给接口服务,坚持必定的神秘感(我只关怀你精干什么,你要什么,你合适什么环境,什么时分用你,怎样用你) 接口阻隔准则:接口多拆拆,防止天主呈现,一同进步复用性 依靠倒置准则:上级要听上级的话,上级派遣下级,下级无权调用上级

规划方式

说完了根本的规划准则,咱们来看到咱们的规划方式,规划方式咱们这儿能够分为三大类。

  1. 构建类型
  2. 结构类型
  3. 行为类型

这个很好了解。由于最早的规划方式是土木老哥那边搞出来的。然后用在咱们这边十分的牛逼。所以能够这样幻想。盖一栋大楼,咱们需求哪些部件,这些部件要怎么组装耦合在一同完成功用。然后呢,这些部件要怎样出产,具有哪些功用。

当然话说回来,由于咱们的实践代码其实是很复杂的,所以的话,咱们其实或许会呈现你中有我我中有你的状况,留意编码即可。

构建类型

工厂方式

okey, 那么接下来咱们一个一个来玩玩。这儿咱们举一个生成轿车的比方:

规划方式-Java版别

规划方式-Java版别

在这儿咱们首要界说了咱们的轿车接口,然后咱们能够出产两种轿车。

public interface ICar {
    void run();
}
public class SuperCar implements ICar {
    @Override
    public void run() {
        System.out.println("我是超跑");
    }
}
public class Truck implements ICar {
    @Override
    public void run() {
        System.out.println("我是卡车");
    }
}

之后的话,咱们创立了一个工厂,这个工厂能够协助咱们生成轿车:

public class CarFactory {
    public static ICar buildCar(Class<? extends ICar> car) throws InstantiationException, IllegalAccessException {
        return car.newInstance();
    }
}

这儿的话,咱们直接经过反射就能够创立一个目标了。所以在运用的时分便是这样:

public class Test {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        ICar superCar = CarFactory.buildCar(SuperCar.class);
        superCar.run();
    }
}

咱们只需求传入到对应的类名即可。当然这儿你会发现咱们每次都是经过反射创立了一个新的目标。那么这个时分实践上咱们能够再优化一下的,还记得咱们的SpringIOC容器嘛。实践上咱们也能够来个简略一点的IOC容器:

public class CarFactory {
    private static Map<Class<? extends ICar>,ICar> IOC = new HashMap<>();
    static {
        IOC.put(SuperCar.class,new SuperCar());
        IOC.put(Truck.class,new Truck());
    }
    public static ICar buildCar(Class<? extends ICar> car) throws InstantiationException, IllegalAccessException {
        return car.newInstance();
    }
    public static ICar getBean(Class<? extends ICar> car){
        return IOC.get(car);
    }
}

咱们这儿仅仅略加改造,加了一个HashMap来保护咱们的目标即可。仅仅与Spring不同的是,咱们这儿选用的是自己手动增加了目标,而Spring是经过xml装备文件或许注解扫包等其他方式来完成了目标的注入。

学过Java反射的咱们都知道,当咱们得到目标的时分,尤其是类的时分,咱们是能够做十分多的操作的,例如咱们的办法增强等等。这儿的话咱们放出一张Spring的Bean周期你就理解了:

规划方式-Java版别
在这儿面BeanDefinition里边存放了一个JavaBean的一些包,类等等信息,经过这个能够得到初始化的目标。然后这儿面有个Aware接口,能够得到一些信息(方便你自己在这边实例化前后做一些操作)之后是PostProcessor。这边便是咱们对办法进行增强了,也便是AOP。由于咱们这儿要履行一些东西,首要仍是靠接口嘛。 当然里边实践上处理到的细节仍是许多的。这儿的话咱们不做过多评论,以后机会话同享一些阅览源码的博客。当然按照我的风格,根本上少不了看完这个东西之后手写一个lite版别的。从前咱们其实写过了一个简略的Spring和MVC,包含一个使命履行结构。

笼统工厂

咱们刚刚是聊到了工厂方式,其实咱们笼统工厂和咱们的工厂是相同的。差异在哪呢,那便是生成的目标不同。实践上,咱们刚刚的工厂方式仅仅一个总称,实践上咱们刚刚的比方仅仅生成的一台轿车。那么实践上,咱们还能够生成轿车工厂。

所以咱们刚刚仅仅生成轿车的工厂叫做:简略工厂 现在在出产制作轿车的工厂的工厂叫做:工厂办法 后来随着企业的状况,不在满足生成轿车,所以咱们又开端出产飞机了,这个时分咱们有一个东西能够出产一个能够出产工厂的工厂,这个时分这个玩意叫做笼统工厂

画个图你就理解了:

规划方式-Java版别
说白了便是一层一层笼统。咱们这儿有个玩笑话:遇事不决,先笼一致层,其实便是这个意思。你知道这儿或许需求这个东西来完成,可是我不知道这个东西详细怎样完成,那没事,先写个接口,不着急界说办法,再写个工厂或许其他的办理器等等,这个东西能够出产或许办理那个接口。之后咱们在评论详细的完成。

这个的话,就比较简略了,说白了便是一层套一层。从简略工厂开端写,再写到尖端的笼统工厂即可。

制作者方式

制作者方式,其实就一句话:化繁为简 仍是拿到咱们刚刚的比方,咱们有一台车。 车有轮子,有发动机,有架子(假定就这写东西的话,那么咱们能够这样干):

首要这个小项目长这个样:

规划方式-Java版别
然后联系长这样:
规划方式-Java版别

代码如下:

public interface CarEngine {
    void engine();
}
public interface CarFrame {
    void frame();
}
public interface CarWheel {
    void wheel();
}
public class CarbonFibreFrame implements CarFrame {
    @Override
    public void frame() {
        System.out.println("碳纤维车架良好");
    }
}
public class MiQIWheel implements CarWheel {
    @Override
    public void wheel() {
        System.out.println("米其林轮胎温度正常");
    }
}
public class V8Engine implements CarEngine {
    @Override
    public void engine() {
        System.out.println("V8引擎发动");
    }
}

之后是咱们的小轿车:

public class GreatCar {
    CarFrame carFrame;
    CarEngine carEngine;
    CarWheel carWheel;
    public GreatCar(CarFrame carFrame, CarEngine carEngine, CarWheel carWheel) {
        this.carFrame = carFrame;
        this.carEngine = carEngine;
        this.carWheel = carWheel;
    }
    public GreatCar() {
    }
    public void start(){
        carEngine.engine();
        carWheel.wheel();
        carFrame.frame();
    }
}

然后的话:

public class Test {
    public static void main(String[] args) {
        GreatCar greatCar = new GreatCar(new CarbonFibreFrame(), new V8Engine(), new MiQIWheel());
        greatCar.start();
    }
}

一辆小轿车完成构建。其实这个方式,再咱们写Controller的时分不自觉就会用到,比方一个Controller里边有许多个Service。

当然,相同的,实践上,你还能够再搞个HashMap,保护这些组件,直接add加入这个组件就能够了。然后遍历处理一下,或许其他的。我自己在完成那个使命小结构的那个使命清单的完成的时分考虑的便是这个玩法。之后,你再合作工厂方式,去搞一下。

原型方式

什么是原型方式呢:用户创立重复的目标,一同还要确保功能的一张规划方式。 这个的话,咱们说实话,没啥东西,便是怎样快速创立目标的一些操作,比方咱们在创立的目标的时分,咱们能够把数据先预处理加载过来创立。在规划类目标的时分多留意模块化规划,然后在创立多个目标的时分,咱们直接用Java的clone,然后重写(假如有需求的话)clone 的办法,给它额定增加数据。

举个比方,咱们创立试卷:

这儿的话,咱们中心便是看到这儿:

public class QuestionBank implements Cloneable{
    private String candidate; // 考生
    private String number;    // 考号
    private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<>();
    private ArrayList<AnswerQuestion> answerQuestionList = new ArrayList<>();
    public QuestionBank append(ChoiceQuestion choiceQuestion) {
        choiceQuestionList.add(choiceQuestion);
        return this;
    }
    public QuestionBank append(AnswerQuestion answerQuestion) {
        answerQuestionList.add(answerQuestion);
        return this;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        QuestionBank questionBank = (QuestionBank) super.clone();
        //对数据进行额定处理(在这儿是打乱标题序号)
        questionBank.choiceQuestionList = (ArrayList<ChoiceQuestion>) choiceQuestionList.clone();
        questionBank.answerQuestionList = (ArrayList<AnswerQuestion>) answerQuestionList.clone();
        // 标题乱序
        Collections.shuffle(questionBank.choiceQuestionList);
        Collections.shuffle(questionBank.answerQuestionList);
        // 答案乱序
        ArrayList<ChoiceQuestion> choiceQuestionList = questionBank.choiceQuestionList;
        for (ChoiceQuestion question : choiceQuestionList) {
            Topic random = TopicRandomUtil.random(question.getOption(), question.getKey());
            question.setOption(random.getOption());
            question.setKey(random.getKey());
        }
        return questionBank;
    }
    public void setCandidate(String candidate) {
        this.candidate = candidate;
    }
    public void setNumber(String number) {
        this.number = number;
    }
}

留意看到他里边从头完成的clone办法即可。也即是说它在从头创立目标的时分做了一个加强

然后直接clone目标:

public class QuestionBankController {
    private QuestionBank questionBank = new QuestionBank();
    public QuestionBankController() {
        questionBank.append(new ChoiceQuestion("JAVA所界说的版别中不包含", new HashMap<String, String>() {{
			//省略创立试卷的进程
    }
    public String createPaper(String candidate, String number) throws CloneNotSupportedException {
        QuestionBank questionBankClone = (QuestionBank) questionBank.clone();
        questionBankClone.setCandidate(candidate);
        questionBankClone.setNumber(number);
        return questionBankClone.toString();
    }
}

单例方式

这个的话是老朋友了,想想咱们常常运用的结构Spring就知道,咱们常常把咱们的这种功用性代码作为一个Bean放到咱们的IOC容器傍边。那么什么东西合适单例呢:

  1. 全局唯一变量
  2. 无状况目标

那么这个代码的话,我就不给了,比较熟悉了。

当然这块的话咱们的单例方式又分为懒汉方式,饿汉方式。然后在并发环境下,还要记得上锁(在懒汉方式下)

结构型

okey,接下来到了咱们的结构型方式了。

适配器方式

这个咱们直接举个十分常见的比方。那便是Mybatis装备多数据源,或许说适应不同的装备的时分。咱们就需求运用到咱们的适配器方式。

留意的咱们的适配器方式和咱们的署理方式有点像,可是意图不同: 适配器方式的目标是完成接口的转化,而署理方式的目标是对目标的拜访进行操控

那么这个的话咱们直接举个十分简略的比方就过去了。咱们举个用老安卓充电线冲type-c的比方。这儿的话咱们搞个转化器(其实便是适配器)就能够完成充电了。

public interface Typec {
    //type-C接口
    void charge();
}
public class AndroidType {
    //安卓充电线
    public void chargeForOlderAndroid() {
        System.out.println("老安卓充电线");
    }
}
public class AdapterForTypeC implements Typec {
	//转化器(适配器)
    private AndroidType androidType;
    public AdapterForTypeC(AndroidType androidType) {
        this.androidType = androidType;
    }
    @Override
    public void charge() {
        androidType.chargeForOlderAndroid();
    }
}

然后装上就能happy了。

public class Test {
    public static void main(String[] args) {
        AdapterForTypeC adapterForTypeC = new AdapterForTypeC(new AndroidType());
        adapterForTypeC.charge();
    }
}

桥接方式

这个方式的话,和咱们搞个写的制作者的代码其实很像,其实就类似的。只不过,这边重组合运用。 在桥接方式中,笼统(接口)和完成的联系是经过桥接接口来建立的。笼统类包含了对完成类的引用,这样就能够调用完成类的办法。笼统类和完成类能够在运行时动态地绑定和替换。

假定咱们有一个 Shape 接口,它界说了一个 draw 办法。咱们还有两个完成类 Circle 和 Rectangle 别离表明圆形和矩形,而且它们都有一个色彩属性。咱们期望能够在不同的色彩下制作不同形状的图形。

public interface Color {
    String getColor();
}

然后咱们界说两个完成类 Red 和 Green 来完成 Color 接口:

public class Red implements Color {
    @Override
    public String getColor() {
        return "Red";
    }
}
public class Green implements Color {
    @Override
    public String getColor() {
        return "Green";
    }
}

接下来,咱们界说 Shape 接口,并引进 Color 接口作为桥接接口:

public interface Shape {
    void draw();
    void setColor(Color color);
}
public class Circle implements Shape {
    private Color color;
    public Circle(Color color) {
        this.color = color;
    }
    @Override
    public void draw() {
        System.out.println("Drawing Circle with " + color.getColor() + " color");
    }
    @Override
    public void setColor(Color color) {
        this.color = color;
    }
}
public class Rectangle implements Shape {
    private Color color;
    public Rectangle(Color color) {
        this.color = color;
    }
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle with " + color.getColor() + " color");
    }
    @Override
    public void setColor(Color color) {
        this.color = color;
    }
}

最终测验

public static void main(String[] args) {
    Color red = new Red();
    Color green = new Green();
    Shape circle = new Circle(red);
    Shape rectangle = new Rectangle(green);
    circle.draw();
    rectangle.draw();
    circle.setColor(green);
    rectangle.setColor(red);
    circle.draw();
    rectangle.draw();
}

其实这个你会发现,咱们在写SpringBoot程序的时分,常常这样干。

组合方式

组合方式,用一颗树的方式来表述出咱们的整个结构。 答应你将目标组合成树状结构来表明“全体-部分”的层次联系。组合方式使得用户对单个目标和组合目标的运用具有一致性。尤其是咱们在优化咱们的if-else判断的时分

举个比方:

if 一个大的状况:
	if 小的状况
		if ...
		else
	else
else if ...
	if ....

这种依托if-else的状况下。 咱们就能够用一个组件优化一下:

if 一个大的状况:
	大的状况的处理器(状况)
else if ...

然后,这个处理器里边你再处理处理。这儿面或许会用到其他的规划方式。 这块的话咱们举个权限验证的比方吧(尽管我很想举个我自己写的那个小结构的比方,可是比较笼统(由于我没有给到对应的背景,读者阅览会比较困难))

okey, 现在咱们来看到咱们一个或许的权限验证的状况:

public boolean hasPermission(User user, String permission) {
    if (user.getRoles() != null) {
        for (Role role : user.getRoles()) {
            if (role.getPermissions() != null) {
                for (Permission p : role.getPermissions()) {
                    if (p.getName().equals(permission)) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

咱们的需求是这样的: 首要的话,咱们又这个Permession,然后有role。然后有User。之后的话,咱们用户里边有规矩。规矩里边有权限。 然后为了一致,咱们都有一个一致接口:

public interface Component {
    boolean hasPermission(String permission);
}

然后有一个界说权限的类:

public class Permission implements Component {
    private String name;
    public Permission(String name) {
        this.name = name;
    }
    @Override
    public boolean hasPermission(String permission) {
        return this.name.equals(permission);
    }
}

之后是咱们的 规矩:

public class Role implements Component {
    private List<Component> components;
    public Role() {
        this.components = new ArrayList<>();
    }
    public void addComponent(Component component) {
        components.add(component);
    }
    public void removeComponent(Component component) {
        components.remove(component);
    }
    @Override
    public boolean hasPermission(String permission) {
        for (Component component : components) {
            if (component.hasPermission(permission)) {
                return true;
            }
        }
        return false;
    }
}

规矩里边也要看权限。(便是看,这个规矩有没有对应到这个权限,咱们在查看用户有没有这个权限的时分,是看到用户的这些规矩里边有没有这个权限,有的话便是true)

然后是咱们的User类:

public class User implements Component {
    private List<Component> components;
    public User() {
        this.components = new ArrayList<>();
    }
    public void addComponent(Component component) {
        components.add(component);
    }
    public void removeComponent(Component component) {
        components.remove(component);
    }
    @Override
    public boolean hasPermission(String permission) {
        for (Component component : components) {
            if (component.hasPermission(permission)) {
                return true;
            }
        }
        return false;
    }
}

最终是咱们的测验代码:

public class Test {
    public static void main(String[] args) {
        Permission permission1 = new Permission("read");
        Permission permission2 = new Permission("write");
        Permission permission3 = new Permission("execute");
        Role role1 = new Role();
        role1.addComponent(permission1);
        Role role2 = new Role();
        role2.addComponent(permission2);
        role2.addComponent(permission3);
        User user = new User();
        user.addComponent(role1);
        user.addComponent(role2);
        System.out.println(user.hasPermission("read"));     // 输出:true
        System.out.println(user.hasPermission("write"));    // 输出:true
        System.out.println(user.hasPermission("execute"));  // 输出:true
        System.out.println(user.hasPermission("delete"));   // 输出:false
    }
}

规划方式-Java版别

装修器方式

这个装修器方式,其实玩过Python的小兄弟应该很熟悉了。咱们有个@语法糖,能够做装修器。它们的思维是类似的:装修器方式也是经过包装目标来完成功用的扩展。在装修器方式中,通常会界说一个装修器类,该类承受一个目标作为参数,在运行时动态地为目标增加额定的行为。经过装修器方式,能够在不改动原始目标的状况下,经过包装目标来扩展其功用。

那么这个时分咱们的装修器方式和署理方式有什么差异: 装修器方式(Decorator Pattern)的意图是在不改动原有目标的基础上,动态地扩展其功用。它答应经过将目标包装在装修器中,来为目标增加额定的行为或职责。 署理方式(Proxy Pattern)的意图是操控对目标的拜访。它供给了一个署理目标,用于在客户端和实践目标之间进行直接拜访,然后能够在拜访目标时增加额定的逻辑。

// 界说一个接口
public interface Component {
    void operation();
}

// 详细的组件完成类,咱们要装修的目标
public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("履行详细组件的操作");
    }
}
// 装修器笼统类
public abstract class Decorator implements Component {
    protected Component component;
    public Decorator(Component component) {
        this.component = component;
    }
    @Override
    public void operation() {
        component.operation();
    }
}

// 详细的装修器类
public class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }
    @Override
    public void operation() {
        super.operation();
        additionalOperation();
    }
    public void additionalOperation() {
        System.out.println("履行额定的操作A");
    }
}
// 详细的装修器类
public class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }
    @Override
    public void operation() {
        super.operation();
        additionalOperation();
    }
    public void additionalOperation() {
        System.out.println("履行额定的操作B");
    }
}

最终是咱们的测验:

public class Test {
    public static void main(String[] args) {
        // 创立详细组件目标
        Component component = new ConcreteComponent();
        // 运用装修器包装组件目标
        Component decoratedComponent = new ConcreteDecoratorA(new ConcreteDecoratorB(component));
        // 履行操作
        decoratedComponent.operation();
    }
}

署理方式

署理方式的话,咱们分为动态署理,和静态署理。这儿的话咱们首要是学习思维,所以咱们这儿就来玩玩静态署理。那么这个静态署理和咱们的装修器方式差异在哪呢。其实差异没有那么大:

在署理方式中,署理类充当的是客户端和实在目标之间的中介,操控客户端对实在目标的拜访。署理类能够在拜访实在目标前后履行额定的操作,而且能够挑选推迟加载实在目标,然后优化功能。署理方式的意图是保护实在目标并供给更高等级的拜访操控。

在装修器方式中,装修器类包装了被装修目标,经过动态地为被包装目标增加新的行为或修正现有行为,然后扩展其功用。装修器方式的意图是增强原始目标的功用,而不是操控对它的拜访。

尽管说两者都是经过外层的类来进行操控的。可是你可了解为主动权不相同。

这儿我就不多说了,能够看到这篇文章,而且的确也是很常见的。

Java Dome(AOP方式回顾小Dome)

咱们能够用署理来完成AOP,切面。

外观方式

一句话:隐藏体系的复杂性,并向客户端供给了一个客户端能够拜访的体系接口。这个活我觉得大部分的人都很喜爱干。比方我,我有个习气,便是每次在开发一个东西的时分,要用到什么第三方,或许什么新的 东西,然后想要完成某些功用的时分它的API调用比较反人类(反我的编码习气)的时分,我就喜爱封装一下,然后给一个接口,我只关怀它要什么,输出什么,什么时分用。举个比方,玩Redis的时分,对redistemplate的api直接做一个封装,要么自己封装,要么嫖现成的,防止关注到太多细节,不利于编码和学习。扯到这个,就不得不提到思维导读在学习傍边的重要性了。

这个咱们常常运用的SpringBoot+MVC的时分,咱们用的注解啥的,便是一个十分直观的比方。

这个详细案例就不给了,太常见了。

享元方式

享元方式(Flyweight Pattern)是一种软件规划方式,它旨在优化许多细粒度目标的内存运用和功能。该方式经过同享目标来削减内存占用,一同进步体系的功能。

在享元方式中,将目标分为可同享的内部状况和不行同享的外部状况。内部状况能够被多个目标同享,而外部状况则取决于特定的目标环境,而且不能被同享。

享元方式的中心思维是运用同享来防止创立许多类似目标,然后削减内存占用。当需求创立一个目标时,首要查看目标池中是否已经存在相同内部状况的目标,假如存在,则直接回来同享的目标;假如不存在,则创立一个新的目标,并将其加入到目标池中以供后续运用。

经过运用享元方式,能够有效地削减体系中目标的数量,下降内存占用,并进步体系的功能。但需求留意的是,在运用享元方式时,要确保同享目标的状况是不行变的,否则或许会导致目标池中的目标状况被修正,然后引发过错。

人话,该共用就共用节省资源,该多例就多例

能够同享的目标,咱们就放在一同进行保护。比方缓存这种(一段时间内是不变的)咱们直接走缓存,大家同享呗,假如不是,或许状况不同,比方每个用户对应的信息订单这种,咱们肯定无法同享。

行为方式

okey,到了咱们这边的行为方式咯。咱们实践的代码都是这些方式好几个混着用的,仍是那句话其实是没有必要分那么清楚,真的会用,你写代码的时分,除非是遇到了某些比较吻合咱们这些规划方式里边的状况的状况下,你或许会自觉的运用外,其实许多时分,你尊从咱们的六个规划准则,很自然就能够写出比较合理的代码。由于实践工程上还要防止过度规划的问题。

职责链方式

说白了便是流水线。假定有一个订单处理体系,订单需求经过多个环节进行处理,包含验证、付出、库存查看和配送等环节。每个环节都有自己的处理规矩和职责。

public abstract class Handler {
    protected Handler nextHandler;
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }
    public abstract void handleRequest(Order order);
}

之后是咱们详细的Handler


public class ValidateHandler extends Handler {
    public void handleRequest(Order order) {
        if (order.isValid()) {
            System.out.println("订单验证经过");
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(order);
            } else {
                System.out.println("订单验证未经过");
            }
        }
    }
}
public class PaymentHandler extends Handler {
    public void handleRequest(Order order) {
        if (order.hasPaid()) {
            System.out.println("订单付出成功");
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(order);
            } else {
                System.out.println("订单付出失利");
            }
        }
    }
}
public class StockCheckHandler extends Handler {
    public void handleRequest(Order order) {
        if (order.hasEnoughStock()) {
            System.out.println("库存查看经过");
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(order);
            } else {
                System.out.println("库存不足");
            }
        }
    }
}
public class ShippingHandler extends Handler {
    public void handleRequest(Order order) {
        if (order.canBeShipped()) {
            System.out.println("订单已发货");
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(order);
            } else {
                System.out.println("订单无法发货");
            }
        }
    }
}

然后是测验代码:

Handler validateHandler = new ValidateHandler();
Handler paymentHandler = new PaymentHandler();
Handler stockCheckHandler = new StockCheckHandler();
Handler shippingHandler = new ShippingHandler();
// 设置处理器的下一个处理器
validateHandler.setNextHandler(paymentHandler);
paymentHandler.setNextHandler(stockCheckHandler);
stockCheckHandler.setNextHandler(shippingHandler);

其实许多结构,比方Spring的Bean周期,Vue的生命周期,其实宏观上面看,便是一个职责链路。

指令方式

将恳求封装成一个目标,然后使得能够运用不同的恳求来参数化其他目标,而且能够将恳求排队或记载日志、撤销等操作。这种方式让恳求发送者和接纳者互相独立,它们不需求知道互相的存在,只需求经过Command目标来进行交互。

举个简略的比方,就比方咱们的遥控器,咱们按下遥控器的按钮,此时发送了指令。然后电视接纳到了指令,完成操作。

所以这个场景(方式)的话咱们是包含了这几个部分的:

  1. 指令(笼统)
  2. 详细指令
  3. 发送者
  4. 接纳者(界说了履行办法)
  5. 客户端,依据指令设置接纳者(履行办法)

这个比方的话,其实我的上一篇博文,netty的一个整合,其实对不不同的消息类型的处理其实便是一个指令方式运用。

现在咱们仍是举个电视机的比方吧:

首要咱们有个TV:

public class TV {
    public void turnOn() {
        System.out.println("电视已开机");
    }
    public void turnOff() {
        System.out.println("电视已关机");
    }
    public void volumeUp() {
        System.out.println("音量增加");
    }
    public void volumeDown() {
        System.out.println("音量减小");
    }
    public void changeChannel() {
        System.out.println("频道切换");
    }
}

然后咱们有个笼统的指令:

public interface Command {
    void execute();
}

之后咱们有好几个详细的指令,这儿我就只贴出一个了:

public class TurnOnCommand implements Command {
    private TV tv;
    public TurnOnCommand(TV tv) {
        this.tv = tv;
    }
    @Override
    public void execute() {
        tv.turnOn();
    }
}

之后是咱们的一个调用者:


public class RemoteControl {
    private Command command;
    public void setCommand(Command command) {
        this.command = command;
    }
    public void pressButton() {
        command.execute();
    }
}

之后的话咱们直接运用:

public class Client {
    public static void main(String[] args) {
        TV tv = new TV();
        Command turnOnCommand = new TurnOnCommand(tv);
        Command turnOffCommand = new TurnOffCommand(tv);
        Command volumeUpCommand = new VolumeUpCommand(tv);
        Command volumeDownCommand = new VolumeDownCommand(tv);
        Command changeChannelCommand = new ChangeChannelCommand(tv);
        RemoteControl remoteControl = new RemoteControl();
        // 设置指令并履行
        remoteControl.setCommand(turnOnCommand);
        remoteControl.pressButton(); // 履行开机指令
        remoteControl.setCommand(volumeUpCommand);
        remoteControl.pressButton(); // 履行增加音量指令
    }
}

这个时分你发现其实和咱们的署理(装修器)方式有那么点像,仅仅呢,首要没有对办法增强(也能够增强)然后仅仅把某一个抽离出来。使得在事务上面看起来更加明亮。

迭代器方式

顺着走,其实咱们这边搞个举到了一个职责链的方式,其实有点像,仅仅差异是,当时状况用到了上一个状况(有点DP的意思哈),然后硬编码的方式走你。其实咱们也能够走迭代器,关于简略的一个状况下。那么咱们优化一下,就变成了这个:

首要咱们先笼统出一层,搞一个HandlerIterator最外层的。

java
public class HandlerIterator implements Iterator<Handler> {
    private List<Handler> handlers;
    private int current;
    public HandlerIterator(List<Handler> handlers) {
        this.handlers = handlers;
        this.current = 0;
    }
    @Override
    public boolean hasNext() {
        return current < handlers.size();
    }
    @Override
    public Handler next() {
        Handler handler = handlers.get(current);
        current++;
        return handler;
    }
}

然后迭代处理就能够了

public class Client {
    public static void main(String[] args) {
        List<Handler> handlers = new ArrayList<>();
        handlers.add(new ValidateHandler());
        handlers.add(new PaymentHandler());
        handlers.add(new StockCheckHandler());
        handlers.add(new ShippingHandler());
        HandlerIterator iterator = new HandlerIterator(handlers);
        Order order = new Order();
        // 处理订单
        while (iterator.hasNext()) {
            Handler handler = iterator.next();
            handler.handleRequest(order);
        }
    }
}

中介方式

中介方式和咱们的署理方式其实看上去很像,可是差异很明显的一个便是,咱们的署理方式是一种结构型方式,首要在于目标的一个操控。中介方式是一种行为方式,是对一类目标进行处理。 一个对一个目标,进行操控和增强,一个是对一类目标进行处理。

比方咱们的ORM结构,他便是个中介。多个数据库,多种数据库,衔接到咱们的ORM结构,然后ORM结构为咱们的应用供给服务。

规划方式-Java版别
这个我感觉没啥好说的。

备忘录方式

这个我觉得没啥好说的

  1. 记得数据备份是好习气
  2. 数据预加载也是好习气
  3. 记得Config备份回滚
  4. 版别操控不限于git,代码装备数据也留意

观察者方式

用于在目标之间建立一对多的依靠联系,使妥当一个目标的状况发生改变时,一切依靠它的目标都能够得到告诉并自动更新。

  1. 主题(Subject):也称为被观察者或可观察目标,主题保护了一组观察者目标,并供给了增加、删除和告诉观察者的办法。主题能够是详细的类或接口。
  2. 观察者(Observer):观察者界说了接纳和处理主题告诉的办法。观察者经过订阅主题来注册自己,并在主题状况发生改变时得到告诉。
  3. 详细主题(Concrete Subject)和详细观察者(Concrete Observer):详细主题是主题的详细完成类,详细观察者是观察者的详细完成类。它们完成了主题和观察者的相应接口,并依据事务需求界说详细的行为。

举个简答的比方便是,咱们那个订阅的一个比方,有哪些用户订阅了什么目标之类的。

举个比方咱们现在有个专栏:


public class Column {
    private String name;
    private List<Observer> subscribers;
    public Column(String name) {
        this.name = name;
        this.subscribers = new ArrayList<>();
    }
    public void subscribe(Observer subscriber) {
        subscribers.add(subscriber);
    }
    public void unsubscribe(Observer subscriber) {
        subscribers.remove(subscriber);
    }
    public void notifySubscribers(String article) {
        for (Observer subscriber : subscribers) {
            subscriber.update(article);
        }
    }
}

然后咱们有个观察者(其实便是咱们的用户)

java
public interface Observer {
    void update(String article);
}
public class User implements Observer {
    private String name;
    public User(String name) {
        this.name = name;
    }
    @Override
    public void update(String article) {
        System.out.println("Hi " + name + "! A new article '" + article + "' has been published in the subscribed column.");
    }
}

之后咱们的测验代码,咱们能够看到哪些用户订阅了,然后给告诉等等。

public class Test {
    public static void main(String[] args) {
        // 创立专栏
        Column techColumn = new Column("Technology");
        // 创立用户
        User user1 = new User("Alice");
        User user2 = new User("Bob");
        // 用户订阅专栏
        techColumn.subscribe(user1);
        techColumn.subscribe(user2);
        // 发布新文章
        techColumn.notifySubscribers("Introduction to AI");
        // 用户撤销订阅
        techColumn.unsubscribe(user2);
        // 再次发布新文章
        techColumn.notifySubscribers("Deep Learning Applications");
    }
}

状况方式

它答应目标在内部状况改动时改动其行为。该方式将目标的状况封装成独立的类,并将状况的转化逻辑封装在状况类中。这样,当目标的状况发生改动时,它能够自动切换到适当的状况类,然后改动其行为。

其实许多时分咱们直接用枚举类也是ok的,当然假如你的状况有特别的操作也是okey的。

public interface State {
    void handle();
}
java
public class NormalState implements State {
    @Override
    public void handle() {
        System.out.println("Normal state: Performing normal operations...");
    }
}
public class WarningState implements State {
    @Override
    public void handle() {
        System.out.println("Warning state: Performing warning operations...");
    }
}
public class ErrorState implements State {
    @Override
    public void handle() {
        System.out.println("Error state: Performing error operations...");
    }
}
public class Context {
    private State currentState;
    public Context() {
        // 初始状况为正常状况
        currentState = new NormalState();
    }
    public void setState(State state) {
        currentState = state;
    }
    public void request() {
        currentState.handle();
    }
}

战略方式

它答应在运行时挑选算法的行为。该方式将不同的算法封装成独立的战略类,而且这些战略类完成了相同的接口,以便在运行时能够互相替换运用。

在代码上其实和咱们的装修器方式很像的。只不过,装修器方式在于创立新的目标,而咱们的战略方式相当于替换不同的战略组件。

public interface Strategy {
    void execute();
}

然后咱们这儿界说不同的战略

public class ConcreteStrategyA implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy A...");
    }
}
public class ConcreteStrategyB implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy B...");
    }
}
public class ConcreteStrategyC implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy C...");
    }
}

然后愉快玩耍

public class Context {
    private Strategy strategy;
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }
    public void executeStrategy() {
        strategy.execute();
    }
}

当然实践上,你还能够合作枚举类,然后搞个HashMap,把这些目标都放在一个容器傍边。

然后你也发现了,这个状况方式和战略方式很像,是的,其实仅仅表明的状况不同,一个偏重状况的描绘,一个偏重不同的状况选用不同的算法方案。

模板方式

望文生义,其实便是模板它界说了一个算法的骨架,将一些过程的完成推迟到子类中。这样能够在不改动算法结构的状况下,经过子类对某些过程进行重界说,然后完成算法的定制化。比方我那个使命结构里边的那个使命清单的模板(当然我那个模板本质上仍是一个署理,办法都是动态加上去的)

public abstract class AbstractTemplate {
    public void templateMethod() {
        // 过程1
        step1();
        // 过程2
        step2();
        // 过程3
        step3();
    }
    protected abstract void step1();
    protected abstract void step2();
    protected void step3() {
        System.out.println("Default implementation of step3");
    }
}

然后完成类就这样玩:

public class ConcreteTemplate extends AbstractTemplate {
    @Override
    protected void step1() {
        System.out.println("Step1 implementation in ConcreteTemplate");
    }
    @Override
    protected void step2() {
        System.out.println("Step2 implementation in ConcreteTemplate");
    }
    @Override
    protected void step3() {
        System.out.println("Step3 implementation in ConcreteTemplate");
    }

拜访者方式

拜访者方式的中心思维是将数据结构和操作解耦,使得操作能够独立改变。它经过在数据结构中界说一个公共的承受拜访者的办法,让拜访者目标对数据结构中的每个元素进行拜访,并履行相应的操作。 直接参考MVC规划思维。

总结

在这儿咱们有六大规划法则:

  1. 单一职责
  2. 开闭准则
  3. 里氏替换准则
  4. 迪米特法则
  5. 接口阻隔准则
  6. 依靠倒置准则

之后咱们有23个根本规划方式,咱们对规划方式进行简略分类又分为三大类型。

  1. 创立型
  2. 结构性
  3. 行为型

详细到每一个分类是:

一 创立型

  1. 简略工厂
  2. 工厂办法
  3. 笼统工厂
  4. 制作者方式
  5. 原型方式
  6. 单例方式

二 结构型

  1. 适配器
  2. 桥接方式
  3. 组合方式
  4. 装修器
  5. 署理方式
  6. 外观方式
  7. 享元方式

三 行为型

  1. 职责链方式
  2. 指令方式
  3. 迭代方式
  4. 中介方式
  5. 备忘录方式
  6. 观察者方式
  7. 状况方式
  8. 战略方式
  9. 模板方式 10.拜访者方式

以上便是全部内容了~