客户端设计(中):OOP、SOLID 与设计模式

SOLID 原则:增强面向对象编程 (OOP) SOLID 原则是增强面向对象编程的设计和结构的基本准则。通过遵守这些原则——单一职责、开放/封闭、里氏替换、接口隔离和依赖倒置——开发人员可以创建更加模块化、可扩展和可维护的代码。在本文中,我们将深入研究 SOLID 原则——单一职责、开放/封闭、里氏替换、接口隔离和依赖反转——并探讨它们如何通过实际代码示例提升 OOP 代码的设计和结构。它促进创建小型的、特定于客户端的接口,而不是大型的、单一的接口。通过遵守 ISP,我们可以防止不必要的方法和依赖项对客户端代码的污染,从而实现更干净、更集中的界面。 阅读详情

客户端架构:为什么、什么时候、怎么做https://blog.csdn.net/mix39/article/details/161257993客户端设计(上):MVC/MVP/MVVM 与高内聚低耦合https://blog.csdn.net/mix39/article/details/161257807客户端设计(中):OOP、SOLID 与设计模式https://blog.csdn.net/mix39/article/details/148036409客户端设计(下):场景流派与实战设计方式https://blog.csdn.net/mix39/article/details/161322269?spm=1001.2014.3001.5502

前言

架构模式(MV*)决定了代码的"分工体制",高内聚低耦合决定了代码的"质量下限"。但真正落地的武器是什么?是 OOP、SOLID、设计模式—— 一条线从基础到套路:先学面向对象,再学设计原则,最后用设计模式落地。

8.3 面向对象编程(OOP)

面向对象是设计模式和设计原则的基础。封装、继承、多态三大特性让代码有了边界、复用和扩展能力。但 OOP 不是银弹——继承的耦合、抽象的边界、类膨胀的风险,都是实战中必须面对的问题。

8.3.1 封装、继承、多态

封装——把变化关起来

藏实现细节,只暴露行为。外面只知道"能干什么",不知道"怎么干的"。

封装前:未读数直接暴露 public int unreadCount;
        任何地方都能 unreadCount = -1; // 谁知道传了什么鬼

封装后:private int unreadCount;
        public void setUnreadCount(int count) { 
            if (count < 0) this.unreadCount = 0;
            else this.unreadCount = count;
        }
        // 不合法的值进不来,变化控制在类内部

封装的本质不是藏字段,而是藏变化——字段类型可能改、校验规则可能加、赋值可能有副作用,这些变化被锁在类内部,外部无感。

继承——让子类复用父类的能力

is-a 关系:Dog is an Animal,ChatTab is a Tab。

abstract class TabPage {
    abstract fun onCreateView()    // 子类填
    fun onLoadFinish() { ... }     // 父类提供通用逻辑
}
class ChatTabPage : TabPage() {
    override fun onCreateView() { ... }  // 只写自己特殊的
}

继承的限制:只能继承一个父类,功能有限。Java 单继承决定了你没法同时复用多个父类的能力,这也是为什么组合比继承更灵活。

多态——同一接口,不同行为

父类引用指向子类对象,调用同一方法,执行不同逻辑。

// 多态:同一接口,运行时不同行为
TabPreload preloader = new ChatPreload();
preloader.preloadSync();  // 执行 ChatPreload 的逻辑

preloader = new HomePreload();
preloader.preloadSync();  // 执行 HomePreload 的逻辑

多态的三个前提:继承/实现、方法重写、父类引用指向子类对象。

三者的关系:封装隐藏实现,继承复用能力,多态扩展行为。抽象定义能力——是三者的灵魂。

抽象——提取共性,忽略差异

抽象类/接口只定义"有什么能力",不定义"怎么实现"。

interface TabPreload {
    fun preloadSync()           // 能力:同步预加载
    fun preloadOnVisible()      // 能力:可见时预加载
}
// AbstractTabPreload 提供默认实现
// 具体的 Tab 各自 override 自己的预加载逻辑

抽象的边界:

抽象最适合用在一个通用的、稳定的、跨业务的公共能力上——比如网络框架、缓存组件、日志体系。

抽象不太适合的场景:自定义 View 或 BaseActivity。实际项目中经常出现 BaseActivity 东塞一个 View 接口、西塞一个回调,实现的时候代码流程不直观不好看,子类为了满足抽象契约被迫写一堆空实现。对于 View 相关的能力,用组合或者扩展函数往往比抽象类更灵活。

8.3.2 优缺点

面向对象的优点

  • 建模直观——现实世界就是对象,User、Message、Chat、Contact,脑袋里好映射
  • 复用——继承和组合让公共逻辑写一次
  • 扩展——多态让新类型丢进去不丢老代码
  • 维护——封装让改动范围可控,改一个类的心里有底

不用 OOP 有什么不好

  • C 风格的过程式:一堆函数操作一堆结构体,谁都能改谁都敢改,没有边界
  • 全局变量满天飞,改一个变量不知道影响谁
  • 加功能 = 复制粘贴 + 到处 if-else,三个月后人就不认识了

继承的缺点

  • 耦合:子类依赖父类实现细节,父类改一个字段,子类全崩
  • 脆弱基类:父类觉得自己的方法子类不需要,删了——结果子类偷偷在用
  • 继承层次深:TabPage → ChatTabPage → ChatTabPageV2 → V3,四层下去你想改 V1 的逻辑,中间 V2 怎么办?继承层级尽量浅,深了就该考虑组合替代
  • 容易被子类篡改:protected 方法被子类乱 override,父类的行为不可控,复杂情况下看不出程序走向造成失败
  • 单继承限制:Java 只能继承一个类,继承了 A 就没法继承 B
  • 编译时绑定:继承关系在编译期就定死了,运行时不能换爹
  • 类膨胀:组合替代继承后,Feature 类会变多

怎么解决继承的缺点

方案

怎么做

组合替代继承

TabPage 持有 Feature 列表,要什么能力加什么 Feature,不要就不加

接口替代抽象类

用接口定义能力,默认实现用 extension/default method,不硬绑定继承链

委托模式

不继承,持有对象并转发调用,想换实现随时换

模板方法谨慎用

父类只定流程骨架,hook 方法尽量少,子类只填关键步骤

泛型优化

有缺点的业务场景用泛型约束类型,避免继承链过长

设计模式优化

策略模式替代继承中的算法变化,状态模式替代继承中的状态变化

接口代替继承

部分场景直接用接口定义行为,不需要抽象类的默认实现

类膨胀怎么解决

组合替代继承后,Feature 类会变多——BadgeFeature、PreloadFeature、SkinFeature……

手段

说明

Feature 注册表

用 Map<String, Feature> 管理,按需取,不需要的 Tab 不注册

Feature 工厂

根据 Tab 类型从工厂取 Feature 组合,而不是每个 Tab 写一套

默认实现

大部分 Feature 行为一样,提供 DefaultBadgeFeature,只有特殊 Tab override

消灭空实现

如果一个 Feature 80% 的子类都是空实现,说明这个抽象没意义,降级为可选接口

8.3.3 重写 vs 重载

重写 (Override)

重载 (Overload)

是什么

子类重新实现父类的方法

同一个类里方法名相同、参数不同

签名

必须完全一致(方法名 + 参数 + 返回值)

方法名相同,参数类型/个数/顺序不同

返回值

相同或是协变返回类型(子类)

随意,跟重载无关

访问修饰符

不能比父类更严格

随意

异常

只能抛父类声明的异常或其子类

随意

绑定时机

运行时(多态)

编译时(静态)

注解

@Override

无特殊注解

// 重写:子类改了父类的行为
class TabBarController {
    void onTabChanged(String tabId) { /* 通用逻辑 */ }
}
class CustomTabBarController extends TabBarController {
    @Override
    void onTabChanged(String tabId) { /* 特殊逻辑 */ }
}

// 重载:同一个类,同名不同参
class BadgeManager {
    void showBadge(String tabId) { ... }                  // 只传 tabId
    void showBadge(String tabId, BadgeInfo info) { ... }  // 传 tabId + 数据
    void showBadge(String tabId, int num, String text) { ... } // 传详细参数
}

坑:重载是编译时绑定的,别以为重载能实现多态——它不能。多态靠重写。


8.4 设计原则(SOLID + 实战扩展)

开发过程中应以实际业务需求为中心,大部分时候需要做到:类职责单一、结构清晰、高内聚低耦合、细的颗粒度、灵活、高的可拓展性、封装变化、能稳健的迭代功能、低成本维护、小的包体积。

使用面向对象语言的特点,让软件达到抽象和细的颗粒度的效果。

SOLID 六大原则速查:

原则

一句话

违反了会怎样

SRP 单一职责

一个类只负责一种功能

改一个功能改三个类

OCP 开闭

加功能不改老代码

加类型改工厂

LSP 里氏替换

子类能替换父类,行为不变

程序跑着跑着炸了

DIP 依赖倒置

细节依赖抽象,别反过来

换实现要改上层

ISP 接口隔离

大接口拆小接口

实现类一堆空方法

LOD 迪米特

只跟直接朋友说话

改个字段连锁反应

8.4.1 术语

术语

定义

抽象

接口或抽象类——定义"能做什么",不定义"怎么做"

细节

实现类——实现接口或继承抽象类而产生的类

高层模块

调用端——发起调用的那一层

底层模块

具体实现类——被调用的那一层

理解这四个术语是读懂 SOLID 的前提——尤其 DIP 里说的"高层不依赖底层,都依赖抽象",搞清楚谁是高层谁是底层才不会晕。

8.4.2 单一职责原则(SRP)

定义:一个类,应该仅有一个引起它变化的原因。一个类应该是一组相关性很高的函数、数据封装。

实现方式:一个类只负责一种类型的功能。

为什么要用:

项目需要不断升级以满足用户需求,升级过程中需要保证系统的稳定性和灵活性。随着功能增多,类会越来越庞大,功能和代码都很复杂,修改的时候不好找改动点。如果耦合性太高,回归验证的时候测试范围会增大。因此需要考虑代码结构的可拓展性、灵活性、降低功能类耦合性。

违反 SRP 的信号:

信号

说明

一个类有多个原因变

既改 UI 逻辑又改数据逻辑,应该拆

一个类超过 1000 行

大概率职责不单一

改一个功能要改同一个类的多处

职责交织了

类名带 Manager/Util/Helper

这类名字通常是职责不单一的信号

8.4.3 开闭原则(OCP)

定义:软件中的对象(类、模块、函数等)应该对于拓展是开放的,对于修改是封闭的。

实现方式:将公共方法抽象成一个接口功能类,子类再去多种多样的拓展实现。已存在的实现类对于修改是封闭的,但是新的实现类可以通过覆写父类的接口应对变化。

为什么要用:

程序一旦开发完成,一个类的实现只应该因错误修改。新的或者改变的特性应该通过新建不同的类实现,新建的类可以通过继承的方式来重用原类的代码。

实操要点:

  • 不是绝对不改老代码——bug 当然要改,OCP 是说加功能尽量不改老代码
  • 抽象是关键——接口/抽象类稳定,实现类可替换,加功能加实现类
  • 变化点预判——哪里可能变,就在哪里留扩展点;确定不变的别过度抽象

8.4.4 里氏替换原则(LSP)

定义:所有引用基类的地方必须能透明地使用其子类的对象。

实现方法:子类可以替换父类(抽象)。通过抽象建立规范,具体的实现在运行时替换掉抽象。

为什么要用:

优点:

  • 代码重用,减少创建类的成本,每个子类都引用父类的方法和属性
  • 子类与父类基本相似但与父类有所区别
  • 提高代码的可拓展性

代价:

  • 继承是侵入性的,只要继承就必须拥有父类的所有属性和方法
  • 可能造成子类代码冗余,降低灵活性

实例:TextView 和 ImageView 可以替换 View

// 窗口类
public class Window {
    public void show(View child) {
        child.draw();
    }
}

// 建立视图抽象,测量视图的宽高为公用代码,绘制实现交给具体的子类
public abstract class View {
    public abstract void draw();
    public void measure(int width, int height) {
        // 测量视图大小
    }
}

// 文本控件类的具体实现
public class TextView extends View {
    public void draw() {
        // 绘制文本
    }
}

// ImageView 的具体实现
public class ImageView extends View {
    public void draw() {
        // 绘制图片
    }
}

// Window 不关心传入的是 TextView 还是 ImageView
// 只要知道它是 View 就能调用 draw()——这就是里氏替换

违反 LSP 的典型表现:

  • 子类 override 了父类方法但返回了不同的行为(父类说返回正数,子类返回负数)
  • 子类抛出了父类没声明的异常
  • 子类的方法是空实现(我不需要这个功能但被迫继承)→ 应该用接口隔离

8.4.5 依赖倒置原则(DIP)

定义:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。

实现方法:

  1. 细节应依赖于抽象。面向接口编程,面向抽象编程
  2. 高层模块不应该依赖低层模块,两者都应该依赖于抽象
  3. 抽象不应该依赖于细节,细节应该依赖抽象

为什么要用:

解耦、高可拓展性。如果类与类依赖于细节,之间就会存在直接耦合,当具体实现需要变化时,意味着要同时修改依赖者的代码,限制系统的可拓展性。

实例:Mother 读任何内容

// 依赖倒置原则:要依赖抽象,不要依赖于细节。针对接口编程,不针对实现编程

public interface IReader {
    public abstract void getContent();
}

public class Book implements IReader {
    @Override
    public void getContent() {
        System.out.println("Reading book");
    }
}

public class NewsPaper implements IReader {
    @Override
    public void getContent() {
        System.out.println("Reading newspaper");
    }
}

public class Mother {
    void read(IReader reader) {
        reader.getContent();
    }
}

public class Main {
    public static void main(String[] args) {
        Mother mother = new Mother();
        mother.read(new Book());
        mother.read(new NewsPaper());
        // 未来加 Magazine?实现 IReader 即可,Mother 不用改
    }
}

DIP 在客户端的体现:

  • BadgeManager 依赖 IBadgeProvider(接口),不依赖 CdpBadgeProvider(实现)
  • TabLauncher 依赖 ITabPreload(接口),不依赖具体的 PreloadImpl
  • Activity 依赖 ViewModel(抽象),不依赖 Repository(实现)

8.4.6 接口隔离原则(ISP)

定义:客户端不应该依赖它不需要的接口,类之间的依赖关系应该建立在最小的接口上。

实现方法:将接口颗粒化。将非常庞大、臃肿的接口拆分成更小的更具体的接口。

为什么要用:

  1. 让客户端依赖的接口尽可能的小,使用方只需要他们使用的接口
  2. 系统解开耦合,从而更容易重构、更改和重新部署

违反 ISP 的典型表现:

  • 实现类有大量空方法(被迫实现接口里不需要的方法)
  • 一个接口的方法超过 7 个,可以考虑拆
  • 调用方只用了接口 3 个方法中的 1 个,但被迫依赖整个接口

ISP vs SRP:

  • SRP 是类的维度——一个类只负责一件事
  • ISP 是接口的维度——一个接口只暴露调用方需要的方法
  • 两者经常一起出现:大类拆小类(SRP),大接口拆小接口(ISP)

8.4.7 迪米特原则(LOD)

定义:一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现与调用者或者依赖者没有关系,调用者或者依赖者只需要知道它需要的方法即可。

实现方法:调用者和依赖者只需要知道要调用的方法,不需要知道具体的细节。

为什么要用:低耦合,让各个类职责更加清晰,调用方无需感知到他不需要关注的细节。

迪米特的通俗理解:

  • 只跟直接朋友说话,不跟陌生人说话
  • 一个方法只应该调用:自己的字段、自己的方法、方法参数的方法、自己创建的对象的方法
  • 不要链式调用:a.getB().getC().doSomething()——你不需要知道 B 和 C 的存在

违反 LOD 的典型具现:

  • 链式调用过深:user.getAccount().getBadge().getCount()——改了 Badge 的结构,User 那边也要改
  • 依赖了不需要知道的细节:调用方拿到了被调方的内部对象

修正方式:

// 违反 LOD
int count = user.getAccount().getBadge().getCount();

// 符合 LOD —— User 提供方法,调用方不需要知道 Badge
int count = user.getUnreadCount();

8.4.8 实战扩展原则

原则

一句话

组合复用原则

优先组合,少用继承

KISS

能简单就别复杂,过度设计比没设计还坑

YAGNI

你不会需要它——没来需求别提前搞

DRY

别重复自己,但提前抽象比重复更危险

SOLID 不是教条——一个 200 行的类不用拆成 5 个,一个 if-else 不用硬改成策略模式。SOLID 是方向,不是标准答案。


8.5 设计模式

代码设计的原因:良好的设计可以在开发前期就规避很多坑。

设计模式的代价:大部分时候使用设计模式都会增加类的数量,消耗内存。单例模式是个例外——它减少内存消耗。所以不要为了用模式而用模式,只在必要场景使用。

设计模式分三大类:创建型(怎么建对象)、结构型(怎么组合对象)、行为型(怎么协调对象)。23 种模式不用全记,记自己用过的 + 客户端常见的就够。

8.5.1 创建型

创建型关注对象的创建方式——怎么建、谁来建、什么时候建。

单例模式(常用)

数据存在于整个应用生命周期、多个地方使用。

特点:某个类在应用里只存在一个对象,减少内存消耗。类里面的全局变量跟随应用生命周期,也经常被拿来当应用的临时缓存类。

缺点:

  • 持有 UI 相关代码或者 Context 容易内存泄漏(严重)——传给单例的 Context 最好是 Application Context
  • 经常搭配观察者模式和异步回调,需要注意没有反注册导致的内存泄漏和数据并发问题

使用必要性:必要场景时可以使用,推荐静态内部类和 DCL 方式实现。

六种实现方式:

1. 饿汉式

系统加载这个类的时候就初始化静态全局变量,外部调用时已经且只会存在一个实例。

// 饿汉式:系统加载这个类时就被创建
// 线程安全(JVM 在类加载时保证不会初始化多个 static 对象)
public class SingleHungry {
    private static SingleHungry singleHungry = new SingleHungry();

    private SingleHungry() {}

    public static SingleHungry getInstance() {
        return singleHungry;
    }
}

优点:稳,线程安全。类的生命周期:加载→验证→准备→解析→初始化→使用→卸载,singleHungry 在初始化阶段完成。

缺点:类加载时就创建对象,万一没用到就是浪费资源。(大型项目不建议,除非确认一定会用到)

2. 懒汉式

需要使用的时候再创建,通过 synchronized 保证多线程情况下单例对象的唯一性。

// 懒汉式:需要使用时再创建,节约资源
// 加入 synchronized 变得线程安全
public class SingleLazy {
    private static SingleLazy singleLazy = null;

    private SingleLazy() {}

    public synchronized static SingleLazy getInstance() {
        if (singleLazy == null) {
            singleLazy = new SingleLazy();
        }
        return singleLazy;
    }
}

优点:随用随建,不浪费资源。

缺点:每次调用 getInstance 都会 synchronized,造成不必要的同步开销。第一次加载时才实例化,慢。

3. 双检索 DCL(推荐)

懒汉模式上再加一层判空,只有第一次才会 synchronized。

// DCL:只有第一次使用 synchronized,减少同步开销
// volatile 防止指令重排序
public class SingleTonDoubleCheck {
    private volatile static SingleTonDoubleCheck singleTon = null;

    private SingleTonDoubleCheck() {}

    public static SingleTonDoubleCheck getInstance() {
        if (singleTon == null) {                    // 第一次检查
            synchronized (SingleTonDoubleCheck.class) {
                if (singleTon == null) {            // 第二次检查
                    singleTon = new SingleTonDoubleCheck();
                }
            }
        }
        return singleTon;
    }
}

为什么要 volatile:singleTon = new SingleTonDoubleCheck() 不是原子操作,可能只分配地址但不初始化,导致其他线程拿到的 singleTon 不为 null 但还没初始化完。volatile 保证分配空间的顺序不变。

缺点:第一次加载还是有点慢;volatile 比较重。不过这种 case 概率极小,如果不是复杂场景或 JDK 6 以下,普通 DCL 就够用。

4. 静态内部类(推荐)

// 静态内部类:使用到内部类时才加载,不用不加载,节约空间
// 不用加锁就能达到线程同步(JVM 保证单线程访问)
public class SingleTonHolder {
    private static class SingleTonInnerHolder {
        private static SingleTonHolder instance = new SingleTonHolder();
    }

    private SingleTonHolder() {}

    public static SingleTonHolder getInstance() {
        return SingleTonInnerHolder.instance;
    }
}

优点:不用加锁就线程安全,需要时才创建。

5. 枚举单例

public enum SingletonEnum {
    INSTANCE;

    public void doSomeThing() {
        System.out.println("do something.");
    }
}

Effective Java 推荐,防反射和序列化破坏。Android 中少用。

6. 容器方法实现单例

// 将多个单例统一管理,按 key 取
public class SingletonManager {
    private static Map<String, Object> objMap = new HashMap<String, Object>();

    private SingletonManager() {}

    public static void registerService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }

    public static Object getService(String key) {
        return objMap.get(key);
    }
}

适合系统级单例管理,比如 SystemServiceRegistry。

五种主流写法对比(容器方法属于系统级用法,日常开发较少使用):

写法

线程安全

懒加载

适用

饿汉

✅

❌

简单场景,初始化不耗时

懒汉+同步

✅

✅

性能差,不推荐

DCL

✅

✅

最常用,注意 volatile

静态内部类

✅

✅

最优雅,推荐

枚举

✅

❌

Effective Java 推荐,Android 少用

其他坑:

  • 多进程?每个进程一份,不是真单例
  • 序列化/反序列化会破坏单例 → readResolve()
  • 反射可以破坏单例 → 枚举防住

Android 实例:Application → 全局生命周期单例、InputMethodManager.getInstance() → 系统服务单例、LayoutInflater


构造者模式(Builder)(常用)

AlertDialog、自定义View封装可变参数、Glide、OkHttp。

特点:封装构建复杂对象的过程,对外隐藏构建细节。链式调用,每个 setter 方法都返回自身。

使用场景:作为配置类的构建器将配置的构建和表示分离开。

标准实现(含 Director):

// Builder 抽象出具体步骤
public abstract class Builder {
    public abstract void buildBoard(String board);
    public abstract void buildDisplay(String display);
    public abstract void buildOs();
    public abstract Computer create();
}

// 具体 Builder
public class MacbookBuilder extends Builder {
    private Computer mComputer = new Macbook();

    @Override
    public void buildBoard(String board) { mComputer.setBoard(board); }
    @Override
    public void buildDisplay(String display) { mComputer.setDisplay(display); }
    @Override
    public void buildOs() { mComputer.setOs(); }
    @Override
    public Computer create() { return mComputer; }
}

// Computer 抽象数据模型
public abstract class Computer {
    protected String mBoard;
    protected String mDisplay;
    protected String mOs;
    public void setBoard(String board) { this.mBoard = board; }
    public void setDisplay(String display) { this.mDisplay = display; }
    public abstract void setOs();
}

// Director 封装构建过程,对外隐藏构建细节
public class Director {
    Builder mBuilder = null;
    public Director(Builder builder) { mBuilder = builder; }
    public void construct(String board, String display) {
        mBuilder.buildBoard(board);
        mBuilder.buildDisplay(display);
        mBuilder.setOs();  
    }
}

// 使用
Builder builder = new MacbookBuilder();
Director director = new Director(builder);
director.construct("英特尔主板", "Retina显示器");
Computer computer = builder.create();

简化版(实战更常用):不通过 Director 代理,直接用 Builder 链式调用,每个 setter 返回 this。

// 简化版:省掉 Director,Builder 直接链式调用
Computer computer = new Computer.Builder()
    .board("英特尔主板")
    .display("Retina显示器")
    .os("Mac OS X")
    .build();

Android 实例:AlertDialog.Builder、Glide.with().load().into()、OkHttp Request.Builder().url().build()

什么时候用 Builder:

  • 参数 > 4 个
  • 很多参数可选
  • 参数之间有约束关系(比如设了A必须设B)
  • 构建过程需要多步

工厂方法

创建哪种对象由子类决定。BitmapFactory。

Android 实例:
- BitmapFactory.decodeResource() / decodeFile() / decodeStream()
  根据来源创建 Bitmap,调用方不需要知道怎么解析
- LayoutInflater.from(context)
  根据系统版本返回不同的 LayoutInflater 实现
- ViewModelProvider.Factory
  根据 ViewModel 类型创建不同实例

工厂方法 vs 简单工厂 vs 抽象工厂:

简单工厂

工厂方法

抽象工厂

谁决定创建什么

一个工厂类 switch

子类决定

一族相关对象

开闭原则

不满足(加类型改工厂)

满足(加子类)

满足

复杂度

低

中

高

适用

类型少且稳定

类型会扩展

需要创建一族对象


原型模式(常用)

clone、Intent深拷贝。

特点:clone 方法。当 new 对象消耗资源太大的时候可以使用原型模式节省 new Object 开销,保护性拷贝防止其他多个对象修改到原始对象的原始值。

浅拷贝 vs 深拷贝:

  • 浅拷贝:复制对象本身,但共享内部引用的对象。引用类型的成员变量只复制地址,原始对象和拷贝对象仍然指向同一个对象
  • 深拷贝:不仅复制对象本身,还会递归复制对象内部引用的所有对象。修改拷贝对象不会影响原始对象
// implements Cloneable,override clone
public class WordDocument implements Cloneable {
    private String mText;
    private ArrayList<String> mImages = new ArrayList<String>();

    @Override
    protected WordDocument clone() {
        WordDocument doc = null;
        try {
            doc = (WordDocument) super.clone();
            doc.mText = this.mText;                          // String 类型浅拷贝即可
            doc.mImages = (ArrayList<String>) this.mImages.clone();  // 引用类型需要深拷贝
            return doc;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

注意:

  • 通过 clone 拷贝对象时不会执行构造函数
  • 如果 ArrayList 里存的是 Object 而不是 String,浅拷贝 mImages 列表后,修改 mImages.get(0).属性 仍然会影响原始对象——因为列表里的对象还是同一份引用

什么时候用原型:

  • 创建对象成本高(数据库查询、网络请求的结果)
  • 需要保存对象快照(撤销/恢复场景)
  • 大量相似对象(改一点点就用)

Android 实例:Intent.clone()、Bundle.clone()


8.5.2 结构型

结构型关注对象的组合方式——怎么把不兼容的接口接起来、怎么控制访问、怎么简化调用。

适配器模式(常用)

RecyclerView。

核心:两个不兼容的接口要对接,中间加一层适配。

Android 实例:
- RecyclerView.Adapter → 数据模型适配成 ViewHolder
  数据是 List<Message>,展示是 ItemView,Adapter 就是中间的适配层
- ArrayAdapter → 数组适配成 ListView Item
- FragmentPagerAdapter → Fragment 适配成 ViewPager 页

三种适配器:

类型

怎么做

适用

类适配器

继承被适配类 + 实现目标接口

Java 不推荐(单继承)

对象适配器

持有被适配对象 + 实现目标接口

最常用 ✅

接口适配器

抽象类空实现目标接口,子类按需 override

接口方法太多时

适配器 vs 外观:适配器是让 A 能调 B;外观是让调用 A 更简单。


代理模式(常用)

AIDL。

核心:控制访问或延迟加载,不直接访问目标对象。

Android 实例:
- AIDL → 客户端持有的是 Proxy 对象,不是真正的 Service
  Proxy 把方法调用序列化 → 跨进程传给 Service → 拿回结果反序列化
  调用方完全不知道是本地调用还是远程调用
- ActivityManagerProxy → 系统 Service 的代理
- Binder → Android IPC 的代理模式实现

三种代理:

类型

怎么做

适用

静态代理

手写代理类

代理逻辑固定、代理对象少

动态代理

运行时生成代理类(Proxy.newProxyInstance)+ 反射机制

代理对象多、代理逻辑通用

虚代理

先返回占位对象,真正用到才加载

延迟初始化、大对象加载

AIDL 就是静态代理 + 虚代理的结合:Proxy 代理了远程调用(静态),Stub 提供了占位(虚)。


外观模式(常用)

音视频SDK、MediaPlayer、Context。

特点:将复杂子系统封装为一层接口,隐藏具体实现,做 SDK 的时候经常用到。

注意:设计接口的时候参数尽量低耦合性。

Android 实例:
- MediaPlayer → 内部涉及 AudioFlinger、AudioTrack、解码器、缓冲区管理
  调用方只需要 setDataSource() → prepare() → start()
- Context → 内部涉及 PackageManager、ResourceManager、SharedPreferences、ActivityManager
  调用方只需要 context.startActivity() / context.getString() / context.getSharedPreferences()
- 音视频SDK → 内部涉及编解码、渲染、音频焦点、网络拉流
  调用方只需要 sdk.play(url) / sdk.stop()

外观 vs 适配器:

  • 适配器:A和B不兼容,中间加一层让它们能对话
  • 外观:A想用B,但B太复杂,中间加一层让A更好用

装饰器模式

动态加功能,不改原类。

Android 实例:
- InputStream → FileInputStream → BufferedInputStream → DataInputStream
  一层包一层,每层加一个功能
- Context → ContextWrapper → Activity / Service / Application
  ContextWrapper 装饰了 Context,加了一层委托
- RecyclerView.ItemDecoration → 给 Item 加装饰(分割线、间距)

装饰器 vs 代理:

  • 代理:控制访问,调用方不知道真实对象存在
  • 装饰器:加功能,调用方知道被装饰对象存在

8.5.3 行为型

行为型关注对象之间的通信——谁通知谁、谁处理谁、状态怎么变、流程怎么走。

观察者模式(常用)

等待数据、被动通知、一对多通知数据、长链接消息、广播、EventBus、MVVM、adapter.notifyDataSetChanged()。

这是客户端用得最多的模式,没有之一。

特点:解耦。一对多消息通知。

缺点:容易被某个观察者卡住通知发不出去。

推模式 vs 拉模式:

推模式——不管观察者是否需要,推送信息通常是主题对象的全部或部分数据通过 update 的参数传递给观察者:

// 推模式:被观察者把所有数据推给观察者
public interface Observer {
    void update();
}

public class LaoWang implements Observer {
    WeatherStation weatherStation;
    public LaoWang(WeatherStation weatherStation) {
        this.weatherStation = weatherStation;
    }
    @Override
    public void update() {
        if (weatherStation.getTemperature() < 0) {
            System.out.println("老王穿上了羽绒服!");
        }
    }
}

public class XiaoLi implements Observer {
    WeatherStation weatherStation;
    public XiaoLi(WeatherStation weatherStation) {
        this.weatherStation = weatherStation;
    }
    @Override
    public void update() {
        if (weatherStation.getDampaness() > 50) {
            System.out.println("小李打开空调,开始除湿!");
        }
    }
}

// 被观察者
public class WeatherStation implements WeatherSubject {
    List<Observer> observers = new ArrayList<>();
    private int temperature = 0;
    private int dampaness = 0;

    @Override
    public void registerObserver(Observer o) { observers.add(o); }
    @Override
    public void removeObserver(Observer o) { observers.remove(o); }
    @Override
    public void notifyObserver() {
        for (Observer o : observers) { o.update(); }  // 推:直接调 update
    }
    // setter 里调 notifyObserver
}

拉模式(更好)——被观察者在通知时只传递少量信息(比如自身引用),观察者按需主动拉取:

// 拉模式:观察者按需取数据
public interface Observer {
    void update(WeatherStation weatherStation);  // 传被观察者引用
}

public class LaoWang implements Observer {
    @Override
    public void update(WeatherStation weatherStation) {
        if (weatherStation.getTemperature() < 0) {
            System.out.println("老王穿上了羽绒服");
        }
    }
}

public class XiaoLi implements Observer {
    @Override
    public void update(WeatherStation weatherStation) {
        if (weatherStation.getDampaness() > 50) {
            System.out.println("小李打开空调,开始除湿!");
        }
    }
}

// 被观察者
@Override
public void notifyObserver() {
    for (Observer o : observers) {
        o.update(this);  // 拉:传自身引用,观察者按需取
    }
}

观察者的坑:

  • 内存泄漏——忘记反注册 → 用 WeakReference 或 Lifecycle 感知
  • 事件风暴——一个变化触发 N 个观察者,N 个又各触发 M 个
  • 顺序依赖——观察者 A 和 B 的执行顺序不确定
  • 容易被某个观察者卡住通知发不出去——某个观察者的 update 里有耗时操作或异常,后面的观察者收不到通知

单例 + 观察者 + 异步回调的并发陷阱:

单例里注册观察者、反注册观察者、消息通知,且消息通知时去刷 UI,需要关注:

  1. View 强引用问题——一般可用 WeakReference 解决
  2. 数据并发问题——修改数据后,多个观察者再获取数据时如果 case 比较复杂容易出现并发问题,读脏数据。需要处理好数据同步问题:加锁 / 深拷贝 / final 传参 / ConcurrentArrayList / ConcurrentHashMap / ConcurrentLinkedQueue

Android 实例:BroadcastReceiver、EventBus、LiveData/Flow、ContentObserver、RecyclerView.Adapter.notifyDataSetChanged()、长链接消息推送


责任链模式

类加载双亲委托机制、事件分发、OA系统流程单流转。

特点:请求者和处理者关系解耦。链式编程。

Android 实例:
- 事件分发 → ViewGroup.dispatchTouchEvent → onInterceptTouchEvent → child.dispatchTouchEvent
  不处理就传给下一层,处理了就消费
- ClassLoader 双亲委托 → 先让父 ClassLoader 加载,父加载不了自己来
  避免重复加载 + 安全(核心类不会被篡改)
- OkHttp Interceptor → Request 经历一系列拦截器:Retry → Bridge → Cache → Connect → CallServer
  每个拦截器处理自己的事,处理完传给下一个
- 未读数展示链 → 产品未读 → 疲劳度检查 → 营销未读 → 最终展示
- 有序广播 → 按优先级传递,可拦截

责任链 vs 观察者:

  • 责任链:一个请求,沿链传,被一个处理者消费
  • 观察者:一个事件,广播,所有观察者都收到

备忘录模式

备份状态。

特点:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复。

Android 实例:

  • Activity 的 onSaveInstanceState 和 onRestoreInstanceState——系统在 Activity 可能被销毁前保存状态,恢复时还原
  • TextField 的 Undo/Redo——每次输入保存一个快照,撤销时恢复

策略模式

切换不同的算法、LayoutManager。

核心:同一行为不同实现,运行时切换。

Android 实例:
- RecyclerView.LayoutManager → LinearLayoutManager / GridLayoutManager / StaggeredGridLayoutManager
  同一个 RecyclerView,换 LayoutManager 就换布局方式
- 动画插值器 Interpolator → AccelerateInterpolator / DecelerateInterpolator / LinearInterpolator
  同一个动画,换个插值器换缓动效果
- 未读数优先级策略 → 产品优先 / 营销优先 / 同级竞争
  同一个展示逻辑,换策略换展示结果

策略 vs 状态:

  • 策略:外部选择用哪个算法,算法之间独立
  • 状态:内部状态决定用什么行为,状态之间有转换

模板方法模式(常用)

封装流程。

实现方法:把某个固定的流程按顺序封装到一个 final 函数中,并且让子类能够定制这个流程中的某些或者所有步骤细节,主体流程顺序又不会被打乱。

Android 实例:
- Activity 生命周期 → onCreate → onStart → onResume → onPause → onStop → onDestroy
  Framework 定了流程,子类 override 各步骤
- AsyncTask → onPreExecute → doInBackground → onPostExecute
  流程固定,子类填每步的实现
- ViewPager + PagerAdapter → instantiateItem / destroyItem / isViewFromObject
  翻页流程固定,子类填怎么创建和销毁页面
- BaseFragment → onCreateView / initView / loadData / onLazyLoad
  自定义模板:子类只关心填数据,不关心流程

模板方法 vs 策略:

  • 模板方法:流程固定,步骤可变(继承)
  • 策略:算法整体可换(组合)
  • 想改流程用策略,想改步骤用模板方法

解释器模式

定义语法规则 + 解析(写框架会用到)、PackageManager 解析 AndroidManifest.xml。

特点:常用于简单语言单个表达式封装、解释。会生成大量的类。

Android 实例:
- PackageManager 解析 AndroidManifest.xml
  定义了 <activity> <service> <receiver> <provider> 等语法规则
  PM 解析这些标签 → 构建出 Component 信息
- SharedPreferences 解析 XML
  定义了 <map> <string> <int> <boolean> 等语法规则
  SP 解析这些标签 → 构建出键值对
- 路由框架解析 URI
  scheme://host/path?param=value
  路由框架定义了解析规则 → 解析出目标页面和参数
- 动态化框架
  定义 JSON → UI 的语法规则
  解释器解析 JSON → 渲染成视图树

什么时候用解释器:

  • 你在写框架,需要定义自己的 DSL/配置语法
  • 你在解析固定格式的配置文件
  • 你在做规则引擎,业务逻辑以规则的形式下发

什么时候不用:

  • 规则很少且简单(直接 if-else)
  • 规则频繁变化(解释器改起来麻烦)
  • 性能敏感(解释器比硬编码慢)

状态模式

行为随状态变化。

Android 实例:
- 音频焦点状态 → 持有焦点 / 丢失焦点 / 暂时失去焦点
  不同状态不同播放行为,状态之间有转换规则
- 下载状态 → 等待 / 下载中 / 暂停 / 完成 / 失败
  不同状态不同 UI 和操作
- 播放器状态 → Idle / Preparing / Playing / Paused / Stopped / Error
  状态决定行为,转换有约束

状态模式 vs 策略模式:

  • 策略:外部选算法,算法之间独立无转换
  • 状态:状态决定行为,状态之间有转换规则

命令模式

操作封装,可撤销可排队。

Android 实例:
- Intent → 把"启动一个Activity"封装成对象
  可以延迟执行、可以跨进程传递、可以被拦截
- Handler.post(Runnable) → 把"执行一段逻辑"封装成消息
  可以延迟执行、可以取消、可以按顺序处理
- Thread_POOL.execute(Runnable) → 把"执行一个任务"封装成对象
  可以排队、可以拒绝、可以管理

8.5.4 设计模式有什么用?平时用吗?

你一定在用,只是不知道叫什么名字。

  • 写了个 Callback 接口?——观察者模式
  • 写了个 Singleton?——单例模式
  • 用了 AlertDialog.Builder?——构造者模式
  • 用了 RecyclerView.Adapter?——适配器模式
  • 用了 Intent 跳转?——命令模式
  • 封装了一个统一入口来操作多个子系统?——外观模式
  • 用了 LayoutInflater?——工厂方法
  • Intent.clone()?——原型模式
  • override Activity.onCreate?——模板方法
  • 换 LayoutManager?——策略模式
  • OkHttp Interceptor?——责任链
  • AIDL 跨进程?——代理模式
  • onSaveInstanceState?——备忘录模式

设计模式不是让你刻意去用的,是你在解决问题时自然用到的。 学设计模式是为了有意识地选择,而不是无意识地堆代码。

真正有用的不是模式本身,是模式背后的思想:

  • 单例 → 全局唯一,共享状态
  • 构造者 → 复杂对象分步构建,隐藏细节
  • 工厂 → 把创建和使用分开
  • 原型 → clone 比 new 省,保护性拷贝
  • 适配器 → 不兼容的接口对接
  • 代理 → 控制访问,延迟加载
  • 外观 → 简化复杂系统,SDK 常用
  • 观察者 → 解耦发送方和接收方
  • 责任链 → 请求沿链处理
  • 策略 → 把变化的部分抽出来
  • 模板方法 → 流程固定步骤可变
  • 备忘录 → 备份状态,可恢复
  • 解释器 → 定义语法 + 解析执行
  • 状态 → 行为随状态变化
  • 命令 → 操作封装,可撤销

知道思想,用不用模式都行。不知道思想,用了模式也白搭。

【iSAQB软件架构】软件设计原则与SOLID原则 SOLID 原则 是面向对象编程(OOP)和软件设计领域五个核心设计原则的首字母缩写。它们由 Robert C. Martin (Uncle Bob) 提出并推广,旨在帮助开发者设计出更易于理解、灵活、可维护和可扩展的软件系统。 阅读详情

相关推荐

从零到一:华为iMaster NCE-Campus部署中的Euler OS与FusionInsight深度解析

本文深入解析华为iMaster NCE-Campus部署中Euler OS与FusionInsight的关键技术要点,包括RAID配置、网络绑定技术、安装避坑指南及性能调优方案。通过实战经验和详细配置示例,帮助工程师提升部署效率与系统稳定性,确保华为企业级解决方案的高效运行。

f6g7h8i9j的博客 543

如何3周备战游戏客户端面试:从GameDeveloper-Client-Interview整理的完整学习路线图

作者的核心动机写在 [README.md](https://link.gitcode.com/i/20594c37b88c020f65a2236cd039ada7/blob/380c5003077d050e9565eaaea2a7637a06553853/README.md?utm_source=gitcode_repo_files) 中:**游戏开发领域的知识共享远少于其他方向**,路线图难找、

gitblog_01041的博客 474

别再手动巡检了!用Zabbix 5.0 + SNMPv2自动监控华为S系列交换机(附完整命令集)

本文详细介绍了如何利用Zabbix 5.0与SNMPv2实现华为S系列交换机的自动化监控,告别低效的手动巡检。通过配置SNMPv2c安全基线、核心功能启用及Zabbix Server端深度配置,构建7×24小时监控体系,实时预警接口状态、流量波动等关键指标,大幅提升运维效率与故障响应速度。

weixin_30940783的博客 624

Java面试题--关于 OOP 和设计模式

这部分包含 Java 面试过程中关于 SOLID 的设计原则,OOP 基础,如类,对象,接口,继承,多态,封装,抽象以及更高级的一些概念,如组合、聚合及关联。也包含了 GOF 设计模式的问题。 103)接口是什么?为什么要使用接口而不是直接使用具体类? 接口用于定义 API。它定义了类必须得遵循的规则。同时,它提供了一种抽象,因为客户端只使用接口,这样可以有多重实现,如 List 接口,你可以

ChangWen的博客 6821

保姆级教程:用高云GW2A开发板搭建RISC-V软核处理器(含DDR3内存优化配置)

本文提供了一份详细的保姆级教程,指导开发者使用高云GW2A-LV18PG256C8 FPGA开发板搭建RISC-V软核处理器系统。教程涵盖了从开发环境搭建、PicoRV32软核集成,到关键的DDR3内存控制器配置与优化,并深入讲解了时序约束与系统调试方法,帮助初学者和爱好者实现从逻辑设计到软硬件协同的深度实践。

2a4s6d8f0g的博客 554

CANOCO5.0实现冗余分析(RDA)最详细步骤

在地理及生态领域会常使用RDA分析,RDA的实现路径也有很多,今天介绍一下CANOCO软件的实现方法。1.不同样点和环境因子之间夹角是锐角 是正相关,是钝角是负相关。2.夹角越大 说明相关性较小,夹角越小,相关性越大。得有不同的物种或者样点数值,再加上环境因子数据。3.箭头越长 相关性越大。时间调整到2010年。

Along6的博客 1万+

OOP五大基本原则SOLID介绍与应用

今天说说面向对象设计的五大基本原则。既然是基本原则,那是我们在各层次的模块、接口的设计中都会参考遵守的。熟练运用它们之后,就已经能够大幅提升代码设计的质量。再遇到针对性的情景时,辅助运用编程语言的一些特性,就会发现“咦,这不就是工厂模式么?这不就是装饰器模式么?这不就是适配器模式么?这不就是Java的XX模式么?这不就是Pythonic的XX模式么?”

阿驹的专栏 4446

STM32定时器ARR寄存器详解:从‘水桶接水’到精准测速,你的PWM和编码器都靠它

本文深入解析STM32定时器ARR寄存器的核心原理与应用技巧,从PWM波形生成到编码器测速实战。通过水桶模型和硬件本质的比喻,揭示ARR如何作为定时器的'心跳节拍器',并详细探讨其在动态调频、死区时间配置和高精度PWM中的关键作用。特别针对编码器模式下的ARR配置和溢出补偿算法进行优化建议,帮助工程师提升系统性能和实时性。

weixin_33734785的博客 376

关于 OOP 和设计模式

这部分包含 Java 面试过程中关于 SOLID 的设计原则,OOP 基础,如类,对象,接口,继承,多态,封装,抽象以及更高级的一些概念,如组合、聚合及关联。也包含了 GOF 设计模式的问题。 103)接口是什么?为什么要使用接口而不是直接使用具体类? 接口用于定义 API。它定义了类必须得遵循的规则。同时,它提供了一种抽象,因为客户端只使用接口,这样可以有多重实现,如 List 接口,你可以...

zilong230905的专栏 1134

【四足机器人项目实战】四足机器人嵌入式硬件设计

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、电源管理板设计及制作二、通讯中继板设计及制作三、力控无刷电机驱动板设计及制作1.磁场定向控制FOC控制原理2.电机转子位置、速度检测原理3.硬件CAN通讯原理 前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长! 本文先对四足机器人–嵌入式硬件设计做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章 提示:以下是本篇文章正文内容 一、电

qq_35635374的博客 1万+

NXOpenC#-通用NX对象操作Tag,Handle,TaggedObject介绍

NXOpenC#-通用NX对象操作Tag,Handle,TaggedObject介绍

阿飞2018 1178

认识设计模式SOLID原则

SOLID 是一个缩写词,代表面向对象编程 (OOP) 的五个设计原则,旨在促进更简单、更健壮和可更新的代码。SOLID 缩写中的每个字母都代表了开发易于维护和随时间扩展的软件的原则。SOLID原则是面向对象编程和设计的五项基本指导原则,由罗伯特·C·马丁(Robert C. Martin)提出,用于帮助开发者构建更加健壮、可维护和可扩展的软件系统。:一个类只应承担一种责任。:软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。:所有派生类都应该能够替换其基类。:高层模块不应该依赖低层模块;

hmx224_2014的专栏 1529

第一讲:halcon模板匹配捆绑测量矩形检测芯片针脚

使用halcon模板匹配捆绑测量矩形,检测位置发生变换的芯片针脚缺陷。

uiopmn1的博客 2988

面向对象(OOP)与面向对象设计(OOD)

面向对象的基本原则

米之炼金术师的博客 3038

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

【设计模式】02-SOLID 设计原则

面向对象编程(OOP)是一种广泛应用的编程范式,它鼓励开发者通过对象来模拟现实世界。为了提高面向对象设计(OOD)的质量和可维护性,Robert C. Martin提出了 SOLID 原则,这五个原则构成了编写良好、可扩展和可维护OOP代码的基础。这五个特定的主题贯穿了对模式和软件设计的一般讨论,所以在我们深入研究设计模式之前(我知道你们都很渴望),我们将简要回顾一下SOLID原则是什么。

C与Python实战 1262

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

面向OOP的设计模式

OOP的5大原则到底说的是啥,怎么在设计模式中体现出来的,以及几种常见的设计模式

hit_zjx的博客 413

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

‌C# OOP 实战:MVC架构中的SOLID原则应用‌

在C# MVC(Model-View-Controller)架构中,应用这些原则能显著提升Web应用的质量,例如减少耦合、增强模块化和简化测试。本指南将逐步解释每个SOLID原则,并通过C#代码示例展示其在MVC中的实际应用。通过以上实战指南,您可以在C# MVC项目中有效应用SOLID原则,构建更健壮的应用程序。下面,我们逐步应用每个SOLID原则到C# MVC项目中,使用代码示例说明。以下是一个简化的ASP.NET Core MVC项目片段,展示如何将所有SOLID原则应用于用户管理。

2501_93876125的博客 1634

企业级应用中的 C# 面向对象编程:设计模式与最佳实践

本文探讨了C#面向对象编程在企业级应用中的关键作用,重点介绍了面向对象四大原则(封装、继承、多态、抽象)及其在C#中的实现方式。文章详细解析了三种设计模式(创建型、结构型、行为型)在C#中的应用实例,包括单例模式、工厂方法、适配器模式等。同时提出了面向对象编程的最佳实践,强调遵循SOLID原则、实现高内聚低耦合、采用依赖注入技术,并关注性能优化。这些方法共同构成了构建可维护、可扩展企业应用的核心技术体系。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1091

设计模式之内功心法 SOLID五大原则

内功心法前言意义五经单一原则开闭原则里式替换接口隔离依赖倒置 前言 随着计算机行业的不断发展,人们对于可以给予自身快速成长的技术非常的痴迷。就比如今天要聊的设计模式,学过编程的都知道设计模式,而很少有人会知道它的核心思想是什么,只是一味的照着网络上流行的设计模式 二十三式 依葫芦画瓢。仅做到了知其然而不知其所以然。所以今天来聊聊它出现的意义什么 意义 我们知道一个江湖的组成是非常不容易的,在江湖中也会有很多的门派与帮派,而门派一多就会有各式各样的问题,这时就出来了一个叫做设计模式的东西,来处理门派在发

学习的doge 2241

重温经典设计模式

导语在软工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领域引入到计算机科学的,设计模式是针对软件设计中常见问题的工具箱,其中的工具就是各种经过实践验证的解决方案。即使你从未遇到过这些问题,了解模式仍然非常件有用,因为它能指导你如何使用面向对象的设计原则来解决各种问题。 今天谈一谈设计模式,一名优秀的开发,应该多少都需要了解一些常用的设计模式和使用场景,让我...

猫哥 的沉淀、积累、总结。天天学习,好好向上...c/c++,嵌入式 linux,Android,HarmonyOS,AIOT) 503

PHP为什么要用经典设计模式,知识体系一共包含哪些部分?底层原理是什么?

因为现代 PHP 已经是企业级应用开发语言需要应对复杂性、提升可维护性、支持团队协作框架本身大量使用设计模式设计模式是高质量代码的“最佳实践”

长风破浪会有时的博客 1241

Python:SOLID 面向对象设计原则

在软件工程与面向对象设计(OOD)领域,SOLID 原则被广泛视为构建可维护、可复用、可扩展、易测试的软件系统的核心指导规范。无论是大型工程架构,还是日常 Python 项目,只要涉及类与对象,SOLID 原则都能帮助你减少耦合、提高代码质量。SOLID 是面向对象设计中最核心、最广泛应用的五大原则,它们共同构成评估一个系统是否具备良好设计的重要标准。尽管 Python 是动态语言,不依赖接口声明,也不限制继承方式,但这些原则依然是判断一个对象体系设计是否健壮的重要标准。高层模块不应依赖低层模块;

qq_41176800的博客 905

OOP和软件设计中的五大核心设计原则——SOLID原则

鸭子类型”——如果它走路像鸭子、叫声像鸭子,那它就应该能当鸭子用。子类不能违背父类的行为约定。就像电脑的USB接口(抽象)——鼠标、键盘(细节)都依赖USB标准,而不是电脑直接依赖具体设备。就像一个人只专注做一件事(例如厨师只管烹饪,服务员只管点餐),避免“万能类”导致代码难以维护。就像乐高积木——通过添加新积木(扩展)来构建新功能,而不是拆掉旧积木(修改原有代码)。就像点餐时只选自己吃的菜,而不是被迫接受一份固定套餐(包含不需要的功能)。,是设计模式(如工厂、策略模式)的基础理论支撑。

轩辕霸天L 587

一文读懂23种设计模式

一文读懂23种设计模式

ahtohlove的博客 831

设计模式相关

观察者模式(Observer Pattern) 定义一个对象与其他对象之间的一种依赖关系,当对象发生某种变化的时候,依赖它的其它对象都会得到更新自定义事件绑定addEvent自定义事件触发trigger自定义事件移除removeEvent实现案例GameEvent类工厂模式及观察者模式抽离英雄基类抽离技能基类扩展皮肤类单例模式使用装饰者模式使用代理模式。

weixin_50556117的博客 1114

OOP 思想 和 设计原则 及24种设计模式

oop思想:封装、继承、多态 把组件实现和接口分开,并且让组件具有多态性 面向对象编程是一种解决软件复用的设计和编程方法,这种方法把软件中相近相似的操作逻辑和操作应用数据、状态以类的形式描述出来,以对象实例的形式在软件系统中复用,以达到提高软件开发效率的作用。 封装:封装可以使类具有独立性和隔离性;保证类的高内聚。只暴露给类外部或子类必须的属性...

weixin_30339457的博客 432
上一篇: 安卓demo-折叠屏平行视界适配(embedding方案)
下一篇: 兼容性&低概率&系统问题记录&性能/体验问题
therese_10086
博客等级 码龄7年 121粉丝 35原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值