如何学好设计,做好架构? 设计原则才是关键

本文讲述了从面向对象的四大特性出发,探讨设计原则(如单一设计原则、开闭原则等)在架构设计中的重要性,以及如何通过实例理解和应用这些原则。作者强调设计原则是架构的基础,而设计模式则是原则的应用。
摘要由CSDN通过智能技术生成

不久前我写了两篇关于架构的文章,真没想到能有这么高的关注度,后续也不断有小伙伴私信向我请教设计/架构的学习方式,从他们提问得问题以及表现出的困惑我能感觉到 想学又无从下手,学了又不会用

其实好多小伙伴都认为学习设计就是学习设计模式,这是一个误区,没有底层思想的支持写出来的设计模式无非就是生搬硬套罢了,这里的底层思想其实就是设计原则,而设计原则则是面向对象编程基于现实背景衍生出来的一套规则,用来解决开发中的痛点。

好的架构需要反复进行思考以及设计,今天我将从面向对象为出发点 来分享自己对设计/架构衍变过程的理解,尽量帮你理清背景 抓住本质

文章目录看起来有些枯燥,但有别于其他八股文,跟着流程去学大概率可以短时间内见成效,所以请耐心阅读

目录

  • 1. 面向对象
  • 1.1 四大特性
  • 1.2 诞生背景
  • 2. 六大设计原则才是一切设计的基石
  • 2.1 单一设计原则
  • 2.2 开闭原则
  • 2.3 迪米特法则
  • 2.4 接口隔离原则
  • 2.5 里氏替换原则
  • 2.6 依赖倒置原则
  • 3. 设计模式只是设计原则的产物而已
  • 3.1 设计模式该怎么去学?
  • 3.2 “盐加少许” 只可意会
  • 3.3 自创设计模式
  • 4. 如何做好架构?

1. 面向对象

什么是面向对象? 估计这个问题能难倒一大片同学,相信读完本文你心里应该会有一个合适的答案。先来看下基本定义:

面向对象是一种风格,会以类作为代码的基本单位,通过对象访问,并拥有封装、继承、多态、抽象四种特性作为基石,可让其更为智能。代表语言Java

1.1 四大特性(也有人说三种,不要纠结)

封装

封装也也可称之为信息隐藏。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或者叫函数)来访问内部信息或者数据。举个例子解释下:

class User{
private String idNumber;
private String name;

public String getIdNumber() {
return idNumber;
}

public void setIdNumber(String idNumber) {
this.idNumber = idNumber;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

User类中包含身份证号、姓名等个人信息,这些属性一旦暴露那外界就可以随意修改,进而可能产生安全隐患。此时可通过private修饰符将其隐藏在内部,如果确实需要访问只能通过暴露出来的唯一入口getter,setter方法进行,这一过程就是封装

合理运用封装可以降低模块间依赖关系(松耦合)

继承

“继承”是面向对象中的第二特征,体现了类与类之间的“is-a”关系。当两个类进行继承关联绑定的时候,子类自动具备来自于的父类的属性和行为。可以提升复用性解决模板代码问题,提升开发效率的同时也解决了错写,漏写带来的问题

多态

一句话概括"多态":一个对象多种形态。举个例子说明下:

interface IFruit{
String getColor();
}

class Apple implements IFruit{
@Override
public String getColor() {
return “red”;
}
}

IFruit fruit = new Apple();
fruit.getColor();

通过声明的IFruit类型可以对其实现类Apple进行编程,好处就是扩展性强,当需要替换具体实现Apple时,对IFruit`的操作完全不用改

合理运用多态可以写出易扩展的代码,基于接口而非实现编程开闭原则的核心

抽象

抽象的目的是为了隐藏方法的具体实现,让调用者只需要关心方法提供了哪些方法(功能),并不需要知道这些功能是如何实现的。在Java中体现方式是接口抽象类

接口和抽象类的区别

  • 接口更侧重于功能的设计,并且能将具体实现与调用者隔离,一般要以接口隔离原则设计接口既粒度越细越好
  • 抽象类更侧重于提升复用性,在原有的基础上预留扩展点供开发者灵活实现
  • 区别:接口可以降低模块间耦合性,抽象类可提升复用性。
  • 相同点:均有较好的扩展性,符合开闭原则

tips

面向对象的四大特性相信大家都很熟悉,本小结只是帮大家做一次简单的回忆,关于其背景职责下半问会详细描述

1.2 诞生背景

谈及面向对象必定磨不开面向过程,毕竟它就是由面向过程衍变而来,吸收其大部分优点并解决其痛点。那什么是面向过程呢?基本定义如下:

分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了,更侧重于功能的设计。代表语言C

用代码体现就是下面这样:

#java版面向过程

public class Wallet {
/**
* 余额
/
int balance;
/
*
* 存钱
/
void saveMoney(int money){
balance += money;
}
/
*
* 花钱
*/
void spendMoney(int money){
balance -= money;
}
}

无权限修饰符将内部信息全部暴露,简单粗暴很符合初级程序员的思维,但带来的问题很明显,外部可直接访问balance修改钱包内余额,现象就是"我钱包都没掏出来但里面钱却变少/多了"面向过程在开发中带来的问题远不止这些,所以在此背景下诞生了面向对象 通过面向对象封装特性将面向过程代码做个改进,如下:

#java版面向对象

public class Wallet {
/**
* 余额
/
private int balance;
/
*
* 存钱
/
void saveMoney(int money){
balance += money;
}
/
*
* 花钱
*/
void spendMoney(int money){
balance -= money;
}
}

通过封装特性将balance通过private修饰,这样外部就没有权限直接修改金额,避免误操作带来的未知风险,满足松耦合特性 面向过程编程偏向于功能的开发,简单粗暴难以维护。而面向对象在编程之前需要基于四大特性对功能做建模设计,可以提高代码安全性、复用性、扩展性,更易于维护 既然面向对象这么智能为什么面向过程语言还没有被淘汰?其实面向对象语言的智能是针对我们开发者的,为了能让我们能写出易于维护的代码会多做一步设计,虽然离开发者更了 但离机器确了,毕竟机器只认识0和1而已。C语言规则简单易于形成机器码,所以执行效率高,这也是其没有被淘汰的原因。

小提示:

不要以为用了面向对象语言写出的就是面向对象代码,如果没有利用其特性那可能还是面向过程,比如没有利用权限修饰符、一个类一把梭等等…

2. 六大设计原则才是一切设计的基石

设计原则是基于面向对象思想衍变出来的一些规则,用来解决实际开发中的一些痛点,是所有设计的底层思想,也是我个人认为是设计/架构领域最重要的知识,所以请大家务必掌握好

2.1 单一设计原则

单一原则很好理解,指一个函数或者一个类再或者一个模块,职责越单一复用性就越强,同时能够间接降低耦合性。

案例:本地获取用户信息,提交到网络

fun post(){
//创建数据库访问对象Dao
val userDao = …(这一过程很复杂)

//从本地获取
val age = dao.getAge()
val name = dao.getName()
//…省略大量字段

//将个人信息提交至网络
http.request(age,name,…)
}

以上案例将创建、获取、提交三步操作写到同一个函数中,很显然违背了单一设计原则,面临的问题也很明显,当修改创建、获取、提交任一过程时都会影响到其他二者,千万不要说"我注意一点就不会出错"这种话,因为人不是机器改动就可能出错,此时可以通过单一设计原则做一次重构,代码如下:

fun getUserDao():UserDao{

return dao
}

fun getUserInfo():UserInfo{
val dao = getUserDao()
val userInfo = UserInfo()
userInfo.age = dao.getAge()
userInfo.name = dao.getName()

return userInfo
}

fun post(){
val userInfo = getUserInfo()
//将个人信息提交至网络
http.request(userInfo.age,userInfo.name,…)
}

三步操作被拆至三个函数 互不影响,从根本上杜绝因改动带来的一系列问题。所以使用面向对象语言开发时,不要急着写代码,要优先考虑下模块、类、函数...的设计是否足够单一

2.2 开闭原则

一句话概括开闭原则:对扩展开放,修改关闭。它即充分诠释抽象、多态特性,又是多数行为型设计模式的基础,遍布于各大优秀框架之中,是最重要的一条设计原则,仅这一条原则就能把你的设计能力提高40%

举个例子让大家感受一下:

需求:通过SQLite做CRUD操作

class SQLiteDao{
public void insert() {
//通过SQLite做insert
}
public void delete() {
//通过SQLite做insert
}
}

SQLiteDao dao = new SQLiteDao();
dao.insert();

以上是最简单粗暴的写法,但存在一个致命问题,如果某一天想替换SQLite业务层基本要动一遍,改动就存在出错的可能,并且需要做大量的重复操作

面对以上问题可以利用抽象、多态特性基于开闭原则做出重构,代码如下:

interface IDao{
void insert();
void delete();
}

class SQLiteDao implements IDao{
@Override
public void insert() {
//通过SQLite做insert
}
@Override
public void delete() {
//通过SQLite做insert
}
}

class RoomDao implements IDao{
@Override
public void insert() {
//通过Room做insert
}
@Override
public void delete() {
//通过Room做delete
}
}

//扩展点
IDao dao = new SQLiteDao();
dao.insert();

  • 定义功能接口IDao
  • 定义类SQLiteDao、RoomDao并实现IDao的功能
  • 业务层基于接口IDao进行编程

重构后,当需要将SQLite替换至Room时,只需将注释扩展点SQLiteDao替换成RoomDao即可,其他地方完全不用改动。这就是所谓的扩展开放,修改关闭

业务不断迭代情况下,唯一不变的就是改变,这种背景下我们能做的只有在代码中基于开闭原则多留扩展点以不变应万变。

2.3 迪米特法则

基本概念:不该有直接依赖关系的模块不要有依赖。有依赖关系的模块之间,尽量只依赖必要的接口。

迪米特法则很好理解并且非常实用,违背迪米特法则会产生什么问题?还以2.1面向过程代码举例:

class Wallet{
/**

  • 余额
    */
    int balance;

/**

  • 存钱
    */
    void saveMoney(int money){
    balance += money;
    }

/**

  • 花钱
    */
    void spendMoney(int money){
    balance -= money;
    }
    }

Wallet的设计违背了迪米特法则,毕竟外部只需要savespend功能,将balance暴漏使用者就有权限直接修改其值,可能会对整个Wallet功能造成影响。此时应基于迪米特法则Wallet进行改造,将balance通过封装特性增加private修饰符

迪米特法则单一设计原则很像,前者符合松耦合后者符合高内聚

2.4 接口隔离原则

基本概念:接口的调用者不应该依赖它不需要的接口。

乍一看与迪米特法则很相似。先来看下什么样的接口违背接口隔离原则

interface Callback{
/**

  • 点击事件回调方法
    /
    void clickCallback();
    /
    *

最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【算法合集】

【延伸Android必备知识点】

【Android部分高级架构视频学习资源】

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 22
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值