延后2

Android应用开发岗 面试汇总-设计模式篇

2022-04-06  本文已影响0人  hahaoop

背景

最近在准备面试,结合之前的工作经验和近期在网上收集的一些面试资料,准备将Android开发岗位的知识点做一个系统的梳理,整理成一个系列:Android应用开发岗 面试汇总。本系列将分为以下几个大模块:
Java基础篇Java进阶篇常见设计模式
Android基础篇Android进阶篇性能优化
网络相关、数据结构与算法
常用开源库、Kotlin、Jetpack

注1:以上文章将陆续更新,直到我找到满意的工作为止,有跳转链接的表示已发表的文章。
注2:该系列属于个人的总结和网上东拼西凑的结果,每个知识点的内容并不一定完整,有不正确的地方欢迎批评指正。
注3:部分摘抄较多的段落或有注明出处。如有侵权,请联系本人进行删除。

1 面向对象设计原则

1.1 单一职责原则

单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。通俗的讲:一个类只负责一个职责,从业务层面来讲就是在不同的业务层级上,一个类或方法只负责一个事情。

1.2 开闭原则

软件实体应当对扩展开放,对修改关闭。可以通过“抽象约束、封装变化”来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。

1.3 里氏替换原则

继承必须确保超类所拥有的性质在子类中仍然成立。即:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。详细解释:

1.4 依赖倒置原则

高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程。
依赖倒置原则的实现方法:

1.5 接口隔离原则

应尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含使用者感兴趣的方法。也就是说,要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

  • 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
  • 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

接口隔离原则的实现方法:

1.6 迪米特法则

迪米特法则又叫作最少知识原则。迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

迪米特法则的实现方法:

但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

所以,在运用迪米特法则时要注意以下 6 点。

1.7 合成复用原则

要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

1.8 总结

以上7 种设计原则是软件设计模式必须尽量遵循的原则,是设计模式的基础。在实际开发过程中,并不是一定要求所有代码都遵循设计原则,而是要综合考虑人力、时间、成本、质量,不刻意追求完美,要在适当的场景遵循设计原则。这体现的是一种平衡取舍,可以帮助我们设计出更加优雅的代码结构。

各种原则要求的侧重点不同,下面我们分别用一句话归纳总结软件设计模式的七大原则,如下表所示:

设计原则 一句话归纳 目的
开闭原则 对扩展开放,对修改关闭 降低维护带来的新风险
依赖倒置原则 高层不应该依赖低层,要面向接口编程 更利于代码结构的升级扩展
单一职责原则 一个类只干一件事,实现类要单一 便于理解,提高代码的可读性
接口隔离原则 一个接口只干一件事,接口要精简单一 功能解耦,高聚合、低耦合
迪米特法则 不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度 只和朋友交流,不和陌生人说话,减少代码臃肿
里氏替换原则 不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义 防止继承泛滥
合成复用原则 尽量使用组合或者聚合关系实现代码复用,少使用继承 降低代码耦合

参考链接

2 常见设计模式

由于设计模式共23种,内容较多,本篇只对常见设计模式做简单总结。先通过下表总结:


image.png
image.png

2.1 单例模式

指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

2.1.1 单例模式有 3 个特点:

优点:

缺点:

2.2 建造者模式

指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。

优点:

  1. 封装性好,构建和表示分离。
  2. 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
  3. 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。

缺点:

  1. 产品的组成部分必须相同,这限制了其使用范围。
  2. 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。

2.3 原型模式

2.4 工厂模式

定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。作用主要是用来创建对象。

2.4.1 简单工厂模式

简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”。

    //抽象产品
    public interface Product {
        void show();
    }
    //具体产品:ProductA
    static class ConcreteProduct1 implements Product {
        public void show() {
            System.out.println("具体产品1显示...");
        }
    }
    //具体产品:ProductB
    static class ConcreteProduct2 implements Product {
        public void show() {
            System.out.println("具体产品2显示...");
        }
    }
    final class Const {
        static final int PRODUCT_A = 0;
        static final int PRODUCT_B = 1;
        static final int PRODUCT_C = 2;
    }
    static class SimpleFactory {
        public static Product makeProduct(int kind) {
            switch (kind) {
                case Const.PRODUCT_A:
                    return new ConcreteProduct1();
                case Const.PRODUCT_B:
                    return new ConcreteProduct2();
            }
            return null;
        }
    }

2.4.2 工厂方法模式

优点:

缺点:

应用场景:

模式的结构与实现

工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。本节来分析其基本结构和实现方法。

模式的结构

工厂方法模式的主要角色如下:

  1. 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
  2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

2.4.3 抽象工厂模式

是对工厂方法模式的升级,抽象工厂类负责生成一系列相关产品的创建,而不只是生成同等级的不同产品类。即生成的是相关联的不同等级的产品类。

抽象工厂模式通常适用于以下场景:

优点:

缺点:

2.5 代理模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

优点:

缺点:

2.5.1 分类:

根据代理的创建时期,代理模式分为静态代理和动态代理。

样例:

package proxy;
public class ProxyTest {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.Request();
    }
}
//抽象主题
interface Subject {
    void Request();
}
//真实主题
class RealSubject implements Subject {
    public void Request() {
        System.out.println("访问真实主题方法...");
    }
}
//代理
class Proxy implements Subject {
    private RealSubject realSubject;
    public void Request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        preRequest();
        realSubject.Request();
        postRequest();
    }
    public void preRequest() {
        System.out.println("访问真实主题之前的预处理。");
    }
    public void postRequest() {
        System.out.println("访问真实主题之后的后续处理。");
    }
}

2.5.2 动态代理

也叫接口代理,通过反射实现。能在代码运行时动态地改变某个对象的代理,并且能为代理对象动态地增加方法、增加行为。
实现方式:

样例:

public class JdkFuDao implements InvocationHandler {
    private IPerson target;
    public IPerson getInstance(IPerson target) {
        this.target = target;
        Class<?> clazz = target.getClass();
        return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target, args);
        after();
        return result;
    }
    private void after() {
        System.out.println("双方同意,开始辅导");
    }
    private void before() {
        System.out.println("这里是C语言中文网辅导班,已经收集到您的需求,开始挑选老师");
    }
}
public class ZhaoLiu implements IPerson {
    @Override
    public void findTeacher() {
        System.out.println("符合赵六的要求");
    }
    public void buyInsure() {
    }
}
public interface IPerson {
    void findTeacher(); //找老师
}
public class Test {
    public static void main(String[] args) {
        JdkFuDao jdkFuDao = new JdkFuDao();
        IPerson zhaoliu = jdkFuDao.getInstance(new ZhaoLiu());
        zhaoliu.findTeacher();
    }
}

2.6 适配器模式

将一个类的接口转换成客户端希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器类实现目标类的接口,并继承被适配类,达到在在适配器类中调用目标类的目的。

2.7 装饰器模式

将原有对象传进来并调用原有对象的方法,同时在新类中拓展其他方法,开发中很常用。

2.8 外观模式

将多个对象的多个方法封装到一个方法中进行调用。提供给外部调用类的方法只有一个。即隐藏了实现,用户不需要关心具体的实现。

2.9 组合模式

如Collection(接口)集合,子集合List、Set等。定义了Collection的方法,不同的子类去实现Collection接口的方法。

2.10 模板方法模式

定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。经典的有Android中的Activity回调方法。

2.11 策略模式

该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
实现方式:

2.12 责任链模式

为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。经典的有Android中的View事件处理机制,事件是否向下传递,用当前对象是否需要处理掉,需要则自己消费,不消费,向下传递。

2.13 观察者模式

指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
实现方式:
被观察着保存观察者对象的集合(订阅),当被观察者状态发生改变时,通过调用观察者的回调方法(发布)去通知观察者执行更新操作。具体如下:

2.14 备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。
实现方式:提供一个保存某个对象管理类和发起人,用来发起和管理需要保存状态的对象。

2.15 享元模式

运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

参考链接

上一篇 下一篇

猜你喜欢

热点阅读