Java设计模式的7种设计原则还有很多人不知道

本文深入探讨了设计模式的七大核心原则:开闭原则、依赖倒置原则、职责单一原则、迪米特法则、接口隔离原则、里氏代换原则及合成复用原则。通过实例解析每个原则的应用场景与价值,帮助读者理解如何在复杂多变的业务中运用设计模式提升代码质量。

前言

其实没有设计模式我们也能完成开发工作。但是为什么需要设计模式呢?让你看起来很牛,没错这个算一个。让你的代码层次感分明,可读性强而且容易维护。让你像我一样有更多的摸鱼划水时间。

可能有人说我一个类或者方法就干完的东西,你搞了七八个。当然使用设计模式也是要斟酌的。一些简单稳定的业务也不推荐使用设计模式。设计模式多用于复杂多变的业务或者要求适配性、扩展性更强的场景中。不要为了设计模式而设计模式。

接下来我们结合实际开探讨一下设计模式的一些原则。

 

1、开闭原则

public class Seller {

    public BigDecimal sellCar(Car car) {
        return car.getPrice();
    }
}

上面模拟4S店一个销售在卖车。突然老板搞了一个促销:在双十一要开展打折活动。在sellCar方法内增加一个计算可行吗?这势必影响整个业务,导致所有车都打折。不行不行!那么在Car里面操作?然后你改啊改!结果各种逻辑流程判断。才实现了业务要求。如果后续打折活动结束了或者升级了,你还要再进行各种改动。你发现一个打折让你的代码面目全非、臃肿不堪。上面说了对于复杂而多变的业务使用设计模式就可以解决。

那么设计模式最重要的一个原则就是开闭原则。也就是说一个软件模型实体如类、模块和函数应该对扩展开放,对修改关闭。也就是需要我们将业务行为抽象出来,使用抽象来构建。具体的业务通过抽象的实现来解决。那么我们就搞一个DiscountCar来extends Car.这样sellCar是什么具体的实现就执行什么具体的逻辑。不会影响以前的逻辑,而且不会因为改动原来的代码影响其他逻辑。保证接口可靠性和稳定性。如下:

public class DiscountCar extends Car{
        
    private BigDecimal price;
    private BigDecimal discount;


    @Override
    public BigDecimal getPrice() {
        return price.multiply(discount);
    }

 }

2、依赖倒置原则

还拿上面的例子来说。经过一系列的打折活动4S店的生意蒸蒸日上。老板突然想扩展一下周边,同时压榨一下销售。让他们卖车的同时卖点玻璃水、防冻液之类的。这个需求当然又抛给了苦逼的程序员。sellCar太具体了不能满足需要了。很多情况下你会增加一个卖玻璃水、卖防冻液的方法。如果以后增加了卖大米,甚至买起了鸡蛋饼呢?总不能一直增加方法吧。我们需要考虑这种问题。我们可以抽象所有卖东西的场景。然后我们把卖的物品抽象成了一个抽象化的概念(java对应的是接口,把卖的行为抽象成了sell方法:

public interface Any {


String getName();


BigDecimal getPrice();


}

public class Seller {


public BigDecimal sell(Any any) {
return any.getPrice();
    }
}

这样随便老板以后卖什么你都可以通过该方法进行处理了,只需要关注于Any的实现。

 

3、职责单一原则

4S店销售卖了一段东西后,发现对客户的吸引力度不大。突然脑子比较灵活的老板又想起了电影中的一句台词:少林功夫加唱歌跳舞有没有搞头?对啊你们销售能不能搞搞什么唱、跳、Rap,当然打篮球就不要了别砸坏了车玻璃。但是人与人是不一样的,有的人只会唱,有的人只会跳,有的人可能唱跳Rap都会甚至篮球都很溜。所以为了适配这么多情况,我们必须把每种技能独立出来,根据不同的人来组合这些技能。

public class Seller implements Sing, Jump, Rap {

public BigDecimal sell(Any any) {
return any.doSell();
    }

@Override
public void sing() {
        System.out.println("seller sing ");
    }

@Override
public void jump() {
        System.out.println("seller jumping ");
    }

@Override
public void rap() {
        System.out.println("seller raping ");
    }
}

但是注意一定要适度,根据业务来细分。否则会导致接口过多反而增大开发难度以及代码的复杂度。

 

4、迪米特法则

新的销售方法搞了一段时间后,老板想看看检验一下他这个馊主意的效果。于是就叫了一个销售让他提供一份报表出来看看。那么程序员该如何实现老板查看报表功能呢,很可能有人会这么写:

public class Boss {
    private Seller seller;
    private Report report;


    public void read() {
        seller.apply(report);
    }
}

乍看功能实现了,细看会发现逻辑不对。哪里不对呢?老板已经持有了报表,如果老板已经知道了你的业绩还叫你干什么?这种逻辑肯定是不对的!也就是说Boss直接依赖了Report;而这个Report不应该直接由Boss处理,而应由Seller控制。

public class Boss {
    private Seller seller;
    
    public void read(){
         seller.apply();
    }   
}

public class Seller {
      private Report report;
      
      public void apply(){
         report.show();
      }
}

这种最大化隔离了类与类之间的关系。降低了类之间的耦合。迪米特法则因此又得名最少知道原则。

 

5、接口隔离原则

用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。一个类对一个类的依赖应该建立在最小的接口上。

建立单一接口,不要建立庞大臃肿的接口尽量细化接口,接口中的方法尽量少,尽量细化接口。注意适度原则,一定要适度。不能滥用

就像上面的唱跳 rap,分离是最好的。

 

6、里氏代换原则

这里主要针对类的继承关系而言。比较正式的定义:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2 时,程序P的行为没有发生变化,那么类型 S 是类型 T 的子类型。

在4S店老板眼里,只要新来的能在销售岗位上像销售老手一样卖出汽车,他就是一名合格的销售。感觉这种定义就像一句名言:不管你黑猫白猫,能抓老鼠的都是好猫。

从某种含义上里氏代换有着以下的契约:

1. 子类必须完全实现父类的方法。父类出现的地方子类都可以代替父类。

2. 子类可以有自己的个性定义。里氏替换原则 可以正着用,但是不能反过来用。在子类出现的地方,父类未必就可以胜任。子类一般比父类有个性。

3. 覆盖或实现父类的方法时输入参数可以被放大。如果4S店老板规定基础车谈价的折扣最多九折,销售打个九五折没有问题,打八折老板肯定要跟你说道说道了。

4. 覆写或实现父类的方法时输出结果可以被缩小。同样是15W本来只能卖出给客户一个乞丐版,结果换了个销售结果给出了一辆旗舰版。怕不是过不了试用期哦。

 

7、合成/复用原则

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

如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

欢迎大家关注我的公种浩【程序员追风】,文章都会在里面更新,整理的资料也会放在里面。

 

总结

这七种设计原则是软件设计模式必须尽量遵循的原则,各种原则要求的侧重点不同。其中,开闭原则是总纲,它告诉我们要对扩展开放,对修改关闭;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;单一职责原则告诉我们实现类要职责单一;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合度;合成复用原则告诉我们要优先使用组合或者聚合关系复用,少用继承关系复用。在实际开发中我们可以根据业务来进行设计模式的使用,但是很重要的一点千万不要被这些条条框框束缚了你的手脚。

 

最后

欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

<think>嗯,用户现在关心的是如何在实际项目中合理使用设计模式并避免滥用,这确实是个很实际的问题。从对话历史看,用户已经对设计模式有了基本了解(之前我详细介绍过23种模式的分类和应用场景),现在更关注实践层面的平衡。 用户引用了四份资料,其中[1]和[2]都明确提到要避免过度设计,[3]则强调要根据开发者水平和项目需求选择模式,而[4]的前端装饰器示例其实展示了模式使用的典型场景——在修改原对象的情况下增强功能。这些引用共同指向一个核心:设计模式是工具而非目的。 用户可能正面临这样的困境:团队在新项目中开始大量引入设计模式,导致代码变得复杂难懂。ta需要的是理论定义,而是可落地的判断标准和使用原则。值得注意的细节是,用户特别要求“参考站内引用”,说明ta更信任权威来源而非个观点。 我决定从三个维度构建回答:使用前提(何时该用)、实施原则(如何用好)、警示信号(何时停用)。每个点都要紧扣用户提供的引用依据: - 对于使用前提,重点突出引用[3]的“根据项目需求和团队技能”以及引用[1]的“充分了解原理和适用场景” - 实施原则部分要强调引用[4]展示的装饰器模式精髓——修改原对象,这符合开闭原则 - 警示信号则直接回应引用[2]的警告“避免过度设计和滥用” 特别需要警惕的是单例模式和抽象工厂的滥用案例,这些在项目中很常见。比如引用[1]提到单例适用于资源密集型对象,但很多会误用于普通工具类。我会用具体代码对比展示:同样的缓存功能,用静态工具类实现比强制单例更简洁安全。 最后考虑加入自检清单,把抽象原则转化为可操作的检查项。用户作为开发者应该需要这样的实用工具,比如“当发现为了套用模式而拆分类时立即暂停”这条,直接来源于引用[2]反对“过度设计”的精神。</think>在实际项目中合理使用设计模式并避免滥用,需结合**设计原则**、**场景适配性**和**团队协作**综合判断。以下是基于引用内容的实践建议: --- ### **一、避免滥用的核心原则**(引用[1][2]) 1. **理解本质而非套用模板** - 充分掌握模式的**适用场景**和**副作用**(如单例模式引入全局状态、装饰器增加调用链复杂度[^4])。 - 例:仅当系统中确需**唯一实例**(如数据库连接池)时才用单例模式,而非将所有工具类强制单例化。 2. **优先满足代码质量而非模式数量** - 关注**可读性**(新成员能否快速理解)、**可维护性**(修改是否容易)、**可测试性**(能否独立单元测试)。 - 反例:为简单CRUD操作强行套用抽象工厂+策略模式,导致类膨胀。 3. **遵循「渐进式设计」原则** - 初期用简单方案,随需求演进逐步引入模式(引用[2])。 - 例:需求仅支持MySQL时,直接实例化`MySQLConnection`;后续需扩展Oracle时,再重构为**抽象工厂**。 --- ### **二、合理使用的关键实践** #### 1. **模式选择与场景匹配**(引用[1][3]) | 场景特征 | 推荐模式 | 避免滥用要点 | |-------------------------|------------------------------|-------------------------------| | 需统一创建多个关联对象 | 抽象工厂模式 | 若非多产品族,改用工厂方法 | | 动态添加/移除对象功能 | 装饰器模式(如日志、缓存[^4])| 避免嵌套超过3层,影响可读性 | | 算法策略频繁变化 | 策略模式 | 简单if/else能解决时无需策略 | | 组件间解耦事件通知 | 观察者模式 | 防止循环依赖导致内存泄漏 | #### 2. **团队协作与沟通**(引用[1][3]) - **建立设计模式词典**:统一团队对模式名称和实现的理解(如明确「工厂」指工厂方法还是抽象工厂)。 - **代码审查聚焦设计**:审查时关注: ```java // 反面案例:过度设计的策略模式 public class Payment { private PaymentStrategy strategy; // 仅需两种支付方式时,增加维护成本 // ... } ``` → 简化为枚举实现更合适: ```java public enum PaymentMethod { ALIPAY, WECHAT_PAY } // 满足需求且更简洁 ``` #### 3. **与设计原则联动**(引用[3]) - **SOLID原则优先**:模式应用前先检查是否符合: - **单一职责原则**:装饰器模式拆分日志/缓存功能 - **开闭原则**:策略模式扩展新算法无需修改主类 - **警惕过度解耦**: 若引入模式后导致调用链 $A \rightarrow B \rightarrow C \rightarrow D$(A调用B,B调用C...),需评估是否违反**迪米特法则**(最少知识原则)。 --- ### **三、识别滥用的警示信号** 1. **代码复杂度骤增** - 新增1个功能需修改5个以上的类 - 简单业务逻辑跨越3层以上接口调用 2. **模式与场景错位** - 用**享元模式**缓存仅创建2次的对象 - 用**责任链**处理无分支的线性流程 3. **违背「You Aren’t Gonna Need It」原则** 为「未来可能支持」的需求预置模式(如提前用适配器模式兼容存在的第三方系统)。 --- ### **四、实战平衡建议** > 引用[2]强调:**“根据实际需求和场景灵活选择,避免过度设计”** > 引用[3]建议:**“关注耦合度,优先用组合而非继承”** 1. **评估性价比公式**: $$ \text{模式收益} = \frac{\text{可扩展性提升} + \text{维护成本降低}}{\text{实现复杂度} + \text{团队学习成本}} $$ 当 **收益 < 1** 时慎用模式。 2. **重构优于预先设计**: 初始版本用直接实现,在以下时机重构为模式: - 需求变更导致代码频繁修改时 → 引入**策略/状态模式** - 组件依赖混乱时 → 引入**中介者/外观模式** 3. **编写「模式自检清单」**: ```markdown - [ ] 是否解决了当前痛点? - [ ] 是否显著提升可维护性? - [ ] 团队能否理解此设计? - [ ] 未来3个迭代内需求会用到此扩展性吗? ``` --- **结论**:设计模式如同手术刀,精准使用可提升系统健壮性(如装饰器实现无侵入扩展[^4]),滥用则导致代码臃肿。核心是**以问题驱动而非模式驱动**,在简单实现与过度设计间寻找平衡点[^1][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值