12 数据结构、算法、设计模式

12-1 多叉树

课程贴:https://ceshiren.com/t/topic/11792
多叉树与红黑树

引言

时间复杂度分析

树的插入,删除,查找的时间复杂度与树的高度成正比: O(高度) == O(最大层数 - 1) ,最符合需求的就是完全二叉树,来看下完全二叉树的最大高度:

在这里插入图片描述

完全二叉树除最后一层,每层结点数:1, 2, 4,…, 2k-1
最后一层:1~2(L-1)
完全二叉树的结点数 n :

n >= 1+2+4+8+…+2L-2 + 1

n <= 1+2+4+8+…+2L-2 + 2L-1

利用等比数列求和公式:

在这里插入图片描述

L 的范围是 [log2(n+1), (log2n) +1]。完全二叉树的层数 <= (log2n) +1 ,其高度小于等于 log2n

二叉查找树会出现树的高度过高,从而导致各个操作的效率下降,比如下图只有右孩子的树(退化成了链表):

在这里插入图片描述

为避免这种情况,一般选用平衡二叉查找树:红黑树。

什么是平衡二叉查找树?

平衡二叉树:任意节点左右子树的高度差 <= 1:

  • 完全二叉树
  • 满二叉树
  • 一些非完全二叉树

平衡二叉树:

在这里插入图片描述

非平衡二叉树

非平衡二叉树

早期的 AVL 树是平衡二叉树 + 查找 = 平衡二叉查找树

但为了实际应用,后续的树都没遵从平衡树的定义(比如红黑树),只要树的高度不比 log2n 大很多,都符合定义。

红黑树

红黑树的英文是 Red-Black Tree,简称 R-B Tree。

  • 根节点是黑色。
  • 叶子节点是黑色的空节点(为了简化代码,本章节可以不考虑)
  • 每个红色结点的两个子结点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色结点)
  • 每个节点,从该节点到达其可达叶子节点的所有路径,都包含相同数目的黑色节点
    二叉树结构

为什么红黑树的高度可以稳定地趋近 log2n ?

将红色节点删除,变成下图(四叉树),由于每个节点,从该节点到达其可达叶子节点的所有路径,都包含相同数目的黑色节点,于是四叉树可以转换成完全二叉树,于是仅包含黑色节点的四叉树的高度比包含相同节点个数的完全二叉树的高度还要小

在这里插入图片描述

把红色节点加回去,由于红色节点不能相邻(红色数量 ~= 黑色数量)。红黑树的高度近似 2log2n。

平衡树比较

  • Treap(树堆)、Splay Tree(伸展树)无法避免时间复杂度的退化
  • AVL 树是一高度平衡的二叉树,查找效率非常高,但是为了维持高度平衡,每次插入、删除都要做调整
  • 红黑树做到了近似平衡,维护平衡的成本比 AVL 树低

12-2 递归算法

课程贴:https://ceshiren.com/t/topic/11613

什么是递归?

递归应用非常广泛,比如 DFS 深度优先搜索、前中后序二叉树遍历等等。

假设一群人在排队,你不清楚自己前面有多少人,可以问前面的人:

image

但你前面也不清楚,整体过程就是递归:

在这里插入图片描述

如果用递推公式描述:

f(n)=f(n-1)+1 其中,f(1)=1
int f(int n) {
  if (n == 1) return 1;
  return f(n-1) + 1;
}

什么情况可以使用递归 ?

1.一个问题可以拆分成子问题.
2.这些问题求解思路相同(数据规模不同)
3.拥有终止条件

来看一个问题:

假如 n 个台阶(n >= 3),每次可以走 1 或 2 个台阶,走完 n 个台阶有多少种走法?
比如有 3 个台阶,可以 1,2 或者 2,1 :

f(n) = f(n-1)+f(n-2)  其中,f(1)=1f(2)=2
int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  return f(n-1) + f(n-2);
}

常见问题

深度问题

在这里插入图片描述

重复计算问题

在这里插入图片描述
在这里插入图片描述

public int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  
  // hasSolvedList可以理解成一个Map,key是n,value是f(n)
  if (hasSolvedList.containsKey(n)) {
    return hasSolvedList.get(n);
  }
  
  int ret = f(n-1) + f(n-2);
  hasSolvedList.put(n, ret);
  return ret;
}

内存开销问题

int f(int n) {
  if (n == 1) return 1;
  return f(n-1) + 1;
}

空间复杂度:O(n)

将递归改写为非递归

int f(int n) {
  int ret = 1;
  for (int i = 2; i <= n; ++i) {
    ret = ret + 1;
  }
  return ret;
}
int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  
  int ret = 0;
  int pre = 2;
  int prepre = 1;
  for (int i = 3; i <= n; ++i) {
    ret = pre + prepre;
    prepre = pre;
    pre = ret;
  }
  return ret;
}

原理:函数调用底层原理是栈,可以利用 for 循环模拟入栈和出栈操作

12-3 单例

课程贴:https://ceshiren.com/t/topic/11910

为什么要使用单例

普通的类就像生产机器,可以生产多个实例(动物糕点),就像下图:

在这里插入图片描述

单例设计模式(Singleton Design Pattern)对实例做限制,一个类只允许创建一个对象。

在这里插入图片描述

那么,对类进行限制有什么好处?假设对文件进行写入,同一个对的多个线程同时写入会造成资源竞争问题,比如两个线程同时给一个共享变量加 1 ,共享变量最后的结果可能只加了 1 。

在这里插入图片描述

如果是同一个对象,加入对象级别互斥锁就能解决问题。

在这里插入图片描述

如果是 Java 程序,可以用 synchronized 关键字给写入操作加入互斥锁:

public class Logger {

  private FileWriter writer;

  public Logger() {

    // 创建文件

    File file = new File("/Users/wangzheng/log.txt");

   // 进行追加写入

    writer = new FileWriter(file, true);

  }

  public void log(String message) {

    // 为 write 加入互斥锁

    synchronized(this) {

      writer.write(mesasge);

    }

  }

}

但是,这段代码存在两个问题:

1.FileWriter 本身就是对象安全的,代码多此一举,。

2.这是对象锁,不同线程使用同一对象才有效,若使用不同对象,没有效(如果不理解,见下图)。

在这里插入图片描述

其实问题不难解决,把对象级别锁提升到类级别即可:

public class Logger {

  private FileWriter writer;

  public Logger() {

    File file = new File("/Users/wangzheng/log.txt");

    writer = new FileWriter(file, true);

  }

  public void log(String message) {

    // 使用类级别的锁

    synchronized(Logger.class) {

      writer.write(mesasge);

    }

  }

}

在这里插入图片描述

其实还有很多方案,分布式锁,并发队列等等,但这些方案都比较复杂,而单例模式的思路简单清晰。下面来实现多个单例。

饿汉式单例

顾名思义,饥饿难忍,在类加载时就创建好实例

在这里插入图片描述

很多语言都支持在类加载前就初始化变量的操作,比如 Java 的静态变量和 Python 的类变量,利用这个特性就能实现饿汉式单例。

java 实现

public class IdMaker {

    // Java 静态属性只会加载一次,所以 instance 只会被初始化一次

    // 利用 Java 静态属性,可以避免多线程资源竞争问题

    // 饿汉式:静态属性,在类加载阶段,完成初始化

    private static IdMaker instance = new IdMaker();

    // ID 计数器,默认值是 -1

    private int id = -1;

    // 把 IdMaker 的构造函数,设为私有(用于阻止调用者实例化 IdMaker)

    private IdMaker() {

    }

    // 获取实例

    public static IdMaker getInstance() {

        return instance;

    }

    // 通过 ++ 操作,获取不一样的 ID 值

    public int getId() {

        id++;

        return id;

    }

}

class TestIdMaker {

    public static void main(String[] args) {

        // 获取唯一的 ID

        int id1 = IdMaker.getInstance().getId();

        int id2 = IdMaker.getInstance().getId();

        int id3 = IdMaker.getInstance().getId();

        System.out.println(id1);

        System.out.println(id2);

        System.out.println(id3);

        // 0 1 2

    }

}

python

class IdMaker:

    # python 的类变量会被多个类,实例共享

    __instance = None

    # __id 也是类变量,多个实例或类共享

    __id = -1

    # python 在类加载阶段,通过父类的 __new__ 创建实例,如果我们重写 __new__

    # 就不会调用父类的 __new__ ,就会调用我们写的 __new__ 创建实例

    # __new__ 需要返回一个实例,如果不返回,就不会实例化

    def __new__(cls):

        if cls.__instance is None:

            # 父类的 __new__ ,参数接收一个类名,会返回类的实例

            cls.__instance = super().__new__(cls)

        return cls.__instance

    # 计数器,在获取前,进行 + 1

    def get_id(self):

        self.__id += 1

        return self.__id

def test_id_maker():

    # IdMaker 是单例类,只允许有一个实例

    id1 = IdMaker().get_id()

    id2 = IdMaker().get_id()

    id3 = IdMaker().get_id()

    print(id1, id2, id3)

if __name__ == "__main__":

    test_id_maker()

    # 0 1 2

因为过于饥饿,该实现不支持延迟加载(用到的时候再初始化),如果实例占用资源多会造成初始化的慢,卡。其实,将占用资源多的方法提前(饿汉),有利有弊:

  • 利:避免运行中卡顿,在初始化阶段就能发现错误
  • 弊:提前初始化浪费资源

懒汉式单例

懒汉非常懒惰,在类使用阶段才会创建实例

在这里插入图片描述

懒汉式可以弥补饿汉式缺点,由于在使用实例时才创建,避免初始化阶段卡慢。

Java 实现

public class IdMaker {

    // Java 引用类型的默认属性是 null

    // 在类加载阶段,不进行初始化

    private static IdMaker instance;

    // ID 计数器,默认值是 -1

    private int id = -1;

    // 把 IdMaker 的构造函数,设为私有(用于阻止调用者实例化 IdMaker)

    private IdMaker() {

    }

    // 懒汉式:在获取实例的阶段,进行初始化

    // synchronized 是互斥锁,为了保证在多线程时,只实例化一次

    public static synchronized IdMaker getInstance() {

        // 如果发现 instance 没有被初始化,就完成初始化

        if (instance == null)

            instance = new IdMaker();

        return instance;

    }

    // 通过 ++ 操作,获取不一样的 ID 值

    public int getId() {

        id++;

        return id;

    }

}

class TestIdMaker {

    public static void main(String[] args) {

        // 获取唯一的 ID

        int id1 = IdMaker.getInstance().getId();

        int id2 = IdMaker.getInstance().getId();

        int id3 = IdMaker.getInstance().getId();

        System.out.println(id1);

        System.out.println(id2);

        System.out.println(id3);

        // 0 1 2

    }

}

python 实现

from threading import Lock

class IdMaker:

    # 申请一个线程锁

    __instance_lock = Lock()

    # python 的类变量会被多个类,实例共享

    __instance = None

    # __id 也是类变量,多个实例或类共享

    __id = -1

    # 如果 __new__ 抛出异常,就不允许调用者进行实例化

    def __new__(cls):

        raise ImportError("Instantition not allowed")

    # 类方法不用实例化也能调用,因为我们不允许进行实例化,所以要使用类方法

    @classmethod

    def get_instance(cls):

        # with 会帮我们自动的上锁和释放,不用我们操心

        with cls.__instance_lock:

            if cls.__instance is None:

                # 因为我们的 __new__ 代码不允许进行实例化,所以可以借用父类的 __new__ 进行实例化

                cls.__instance = super().__new__(cls)

        return cls.__instance

    # 计数器,在获取前,进行 + 1

    def get_id(self):

        self.__id += 1

        return self.__id

def test_id_maker():

    # IdMaker 是单例类,只允许有一个实例

    id1 = IdMaker.get_instance().get_id()

    id2 = IdMaker.get_instance().get_id()

    id3 = IdMaker.get_instance().get_id()

    print(id1, id2, id3)

if __name__ == "__main__":

    test_id_maker()

    # 0 1 2

12-4 工厂方法

课程贴:https://ceshiren.com/t/topic/11937

简介

当创建对象的代码多而杂时,可以用工厂模式将对象的创建和使用分离,让代码更加清晰。

比如下面代码根据不同的 rule 创建不同的对象。

java demo

public class Demo {

    // 解析文件,返回解析内容

    public void load(String rule) {

        IParse parse = null;

        // 根据不同的文件类型,执行不同的内容

        // 分析:如果代码中创建对象的过程很复杂,就需要把这段代码移出去,单独封装成类,封装的类就是工厂类

        if ("xml".equals(rule))

            parse = new XmlParse();

        else if ("json".equals(rule))

            parse = new JsonParse();

        else if ("excel".equals(rule))

            parse = new ExcelParse();

        else if ("csv".equals(rule))

            parse = new CsvParse();

        else

            parse = new OtherParse();

        // parse 是解析类之一,具体是哪一个,要根据 rule 来决定

        parse.parse();

        // 省略其它有关 load 的大量操作

    }

    public static void main(String[] args) {

        Demo demo = new Demo();

        // 传入 json

        demo.load("json");

    }

}

// 所有解析类的接口,规定解析类的方法

interface IParse {

    // 需要解析类去实现

    public void parse();

}

class XmlParse implements IParse {

    // 解析结果

    public void parse() {

        System.out.println("XmlParse");

    }

}

class JsonParse implements IParse {

    public void parse() {

        System.out.println("JsonParse");

    }

}

class ExcelParse implements IParse {

    public void parse() {

        System.out.println("ExcelParse");

    }

}

class CsvParse implements IParse {

    public void parse() {

        System.out.println("CsvParse");

    }

}

class OtherParse implements IParse {

    public void parse() {

        System.out.println("OtherParse");

    }

}

python demo

# Demo 用于加载不同的文件,对不同的文件作不同的处理 

class Demo:

    def load(self, rule):

        parse = None

        # 根据不同的 rule ,创建不同的对象

        if "xml" == rule:

            parse = XmlParse()

        elif "json" == rule:

            parse = JsonParse()

        elif "excel" == rule:

            parse = ExcelParse()

        elif "csv" == rule:

            parse = CsvParse()

        else:

            parse = OtherParse()

        # 调用对象的方法进行操作

        parse.parse()

# 相当于接口,用于规范各个解析类

# 每个解析类都要实现 parse 方法,否则在调用的时候就会报错

class IParse:

    def parse(self):

        raise ValueError()

class XmlParse(IParse):

    def parse(self):

        print("XmlParse")

class JsonParse(IParse):

    def parse(self):

        print("JsonParse")

class ExcelParse(IParse):

    def parse(self):

        print("ExcelParse")

class CsvParse(IParse):

    def parse(self):

        print("CsvParse")

class OtherParse(IParse):

    def parse(self):

        print("OtherParse")

if __name__ == "__main__":

    Demo().load("json")

简单工厂

把创建大量实例的代码放到工厂类中。

java 实现

public class Demo {

    // 解析文件,返回解析内容

    public void load(String rule) {

        IParse parse = ParseFactory.createParse(rule);

        // parse 是解析类之一,具体是哪一个,要根据 rule 来决定

        parse.parse();

        // 省略其它有关 load 的大量操作

    }

    public static void main(String[] args) {

        Demo demo = new Demo();

        // 传入 json

        demo.load("json");

    }

}

// 简单工厂:把创建对象的代码移动到这个类中的一个方法,这个类就叫做简单工厂

// 简单工厂的类名字通常以 Factory 结尾

// 简单工厂的方法,通常叫 createParse

class ParseFactory {

    public static IParse createParse(String rule) {

        IParse parse = null;

        // 根据不同的文件类型,执行不同的内容

        if ("xml".equals(rule))

            parse = new XmlParse();

        else if ("json".equals(rule))

            parse = new JsonParse();

        else if ("excel".equals(rule))

            parse = new ExcelParse();

        else if ("csv".equals(rule))

            parse = new CsvParse();

        else

            parse = new OtherParse();

        return parse;

    }

}

    // 所有解析类的接口,规定解析类的方法

    interface IParse {

        // 需要解析类去实现

        public void parse();

    }

    class XmlParse implements IParse {

        // 解析结果

        public void parse() {

            System.out.println("XmlParse");

        }

    }

    class JsonParse implements IParse {

        public void parse() {

            System.out.println("JsonParse");

        }

    }

    class ExcelParse implements IParse {

        public void parse() {

            System.out.println("ExcelParse");

        }

    }

    class CsvParse implements IParse {

        public void parse() {

            System.out.println("CsvParse");

        }

    }

class OtherParse implements IParse {

    public void parse() {

        System.out.println("OtherParse");

    }

}

python 实现

# Demo 用于加载不同的文件,对不同的文件作不同的处理 

# 问题:如果创建对象的代码比如多,可能还会创建 text ,md,yml 等等

# 简单工厂解决:把对象的创建移动到其它类中, load 方法就会很简洁

class Demo:

    def load(self, rule):

        parse = ParseRuleFactory().create_parse(rule)

        # 调用对象的方法进行操作

        parse.parse()

# 简单工厂类:用于实例的创建,根据 rule 创建不同的实例。本质就是把 Demo 中原来创建实例的代码,给迁移过来

class ParseRuleFactory:

    def create_parse(self, rule):

        parse = None

        # 根据不同的 rule ,创建不同的对象

        if "xml" == rule:

            parse = XmlParse()

        elif "json" == rule:

            parse = JsonParse()

        elif "excel" == rule:

            parse = ExcelParse()

        elif "csv" == rule:

            parse = CsvParse()

        else:

            parse = OtherParse()

        return parse

        

# 相当于接口,用于规范各个解析类

# 每个解析类都要实现 parse 方法,否则在调用的时候就会报错

class IParse:

    def parse(self):

        raise ValueError()

class XmlParse(IParse):

    def parse(self):

        print("XmlParse")

class JsonParse(IParse):

    def parse(self):

        print("JsonParse")

class ExcelParse(IParse):

    def parse(self):

        print("ExcelParse")

class CsvParse(IParse):

    def parse(self):

        print("CsvParse")

class OtherParse(IParse):

    def parse(self):

        print("OtherParse")

if __name__ == "__main__":

    Demo().load("json")

工厂方法

如果创建实例的代码非常复杂,就可以把创建实例的代码单独放入一个类。

比如下面的例子,创建实例代码复杂:

java 实现

public class Demo {

    // 如果创建对象时,代码很复杂,简单工厂就不能解决问题,因为即使使用简单工厂,创建实例依旧很复杂

    // 此时就需要工厂方法来解决、

    // 工厂方法解决方案:将每一个创建过程都封装到工厂类中

    // 比如下面的 JsonParseRuleFactory ,将复杂的代码都放到了 JsonParseRuleFactory 类中

    public void load(String rule) {

        IParse parse = null;

        if ("xml".equals(rule))

            // 省略了 1000 行代码

            parse = new XmlParse();

        else if ("json".equals(rule))

            parse = new JsonParseRuleFactory().createParse();

        else if ("excel".equals(rule))

            // 省略了 1000 行代码

            parse = new ExcelParse();

        else if ("csv".equals(rule))

            // 省略了 1000 行代码

            parse = new CsvParse();

        else

            // 省略了 1000 行代码

            parse = new OtherParse();

        // parse 是解析类之一,具体是哪一个,要根据 rule 来决定

        parse.parse();

        // 省略其它有关 load 的大量操作

    }

    public static void main(String[] args) {

        Demo demo = new Demo();

        // 传入 json

        demo.load("json");

    }

}

// 工厂方法的接口:要根据不同的调用,实现不同的操作

interface IParseRuleFactory {

    // createParse 接口要创建对象以及复杂操作

    public IParse createParse();

}

// 把原来 json 处理的所有代码,都迁移过来

class JsonParseRuleFactory implements IParseRuleFactory {

    public IParse createParse() {

        // 省略了 1000 行代码

        return new JsonParse();

    }

}

// 所有解析类的接口,规定解析类的方法

interface IParse {

    // 需要解析类去实现

    public void parse();

}

class XmlParse implements IParse {

    // 解析结果

    public void parse() {

        System.out.println("XmlParse");

    }

}

class JsonParse implements IParse {

    public void parse() {

        System.out.println("JsonParse");

    }

}

class ExcelParse implements IParse {

    public void parse() {

        System.out.println("ExcelParse");

    }

}

class CsvParse implements IParse {

    public void parse() {

        System.out.println("CsvParse");

    }

}

class OtherParse implements IParse {

    public void parse() {

        System.out.println("OtherParse");

    }

}

python 实现

# 问题:简单工厂不能解决创建实例的代码可能很复杂,即使迁移到了简单工厂中,复杂的创建过程依旧存在

# 解决:使用工厂方法,把创建过程封装到工厂类

class Demo:

    def load(self, rule):

        parse = None

        if "xml" == rule:

            # 省略 1000 行代码

            parse = XmlParse()

        elif "json" == rule:

            parse = JsonParseRuleFactory().create_parse()

        elif "excel" == rule:

            # 省略 1000 行代码

            parse = ExcelParse()

        elif "csv" == rule:

            # 省略 1000 行代码

            parse = CsvParse()

        else:

            # 省略 1000 行代码

            parse = OtherParse()

        # 调用对象的方法进行操作

        parse.parse()

# 相当于接口,用于规范各个工厂类

class IParseRuleFactory:

    def create_parse(self):

        raise ValueError()

# 工厂:把 Json 的解析放到此工厂下面

class JsonParseRuleFactory (IParseRuleFactory):

    def create_parse(self):

        # 省略 1000 行代码

        return JsonParse()

# 相当于接口,用于规范各个解析类

# 每个解析类都要实现 parse 方法,否则在调用的时候就会报错

class IParse:

    def parse(self):

        raise ValueError()

class XmlParse(IParse):

    def parse(self):

        print("XmlParse")

class JsonParse(IParse):

    def parse(self):

        print("JsonParse")

class ExcelParse(IParse):

    def parse(self):

        print("ExcelParse")

class CsvParse(IParse):

    def parse(self):

        print("CsvParse")

class OtherParse(IParse):

    def parse(self):

        print("OtherParse")

if __name__ == "__main__":

    Demo().load("json")

抽象工厂

假设有 A 公司, B 公司,C 公司… 都要有自己的解析方法,比如:A 公司有 AXmlParseFactory ,B 公司有 BXmlParseFactory ,C 公司有 CXmlParseFactory 。如果此时使用简单工厂和工厂方法,工作量非常大,因为要给每一个公司都创建所有的解析工厂。

此时可以用抽象工厂解决问题。解析工厂返回多个实例,比如,XmlParseFactory 可以返回 A ,B , C 的实例。

java 实现

// 抽象工厂:使工厂类具有返回多个实例的功能

interface IParseRuleFactory {

    public IParse AcreateParse();

    public IParse BcreateParse();

    public IParse CcreateParse();

}

class XmlParseRuleFactory implements IParseRuleFactory{

    public IParse AcreateParse() {}

    public IParse BcreateParse(){}

    public IParse CcreateParse(){}

}

python 实现

# 问题:如果多个公司都要封装工厂,比如 A, B, C ...公司都要封装自己的工厂,就要封装 n 个工厂类

# 解决:可以使用抽象工厂解决问题,每个工厂类可以创建多个实例,比如 JsonParseRuleFactory ,可以创建 A, B, C 公司的实例

# 一个工厂类,可以生成多个公司的解析方法

class IParseRuleFactory:

    def a_create_parse(self):

        raise ValueError()

    def b_create_parse(self):

        raise ValueError()

    def c_create_parse(self):

        raise ValueError()

    

# 实现时候,一个工厂类就可以生成多个公司的实例

class JsonParseRuleFactory(IParseRuleFactory):

    def a_create_parse(self):

        """

        """

    def b_create_parse(self):

        """

        """

        

    def c_create_parse(self):

        """

        """

12-6 设计模式基本原则解读

在这里插入图片描述

SOLID

英文名中文解释
Single Responsibility Principle单一职责一个类或者模块只负责完成一个职责(或者功能)
Open Closed Principle开闭原则对扩展开放、对修改关闭
Liskov Substitution Principle里式替换子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏
Interface Segregation Principle接口隔离原则客户端不应该被强迫依赖它不需要的接口
Dependency Inversion Principle依赖反转原则高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象。

其它原则

简称英文名中文解释
KISSKeep It Simple and Stupid尽量保持简单保持简单
YAGNIYou Ain’t Gonna Need It你不会需要它,不要做过度设计
DRYDon’t Repeat Yourself不重复不要编写重复的代码
LODLaw of Demeter迪米特法则每个模块只应该了解那些与它关系密切的模块的有限知识
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卢思小姐姐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值