在软件开发过程中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性。要尽量根据6条开发原则来开发程序,从而提高软件开发效率、节约开发成本和维护成本。
一、开闭原则
对扩展开放,对修改关闭。在程序需要进行扩展时,不能去修改原有的代码,而是增加新的接口来对功能进行升级,实现一个热插拔效果,总而言之,是为了是程序的扩展型提高,便于维护和迭代更新。
而想实现这样的效果,我们需要使用接口和抽象类。
之所以使用它们,是因为抽象类与接口的灵活性好,只要抽象方法合理,可以保持软甲架构的稳定。而软件中的容易发生变动的细节可以将其抽象出来,来进行扩展,当软件需求发生变化时,只需要在派生一个类即可实现新需求。即写一个新的实现类来实现原来的接口
基类 --包含main方法
package com.xiao.principles.opendemo;
import com.xiao.principles.opendemo.impl.DefaultOpen;
import com.xiao.principles.opendemo.impl.NewOpen;
import java.util.HashMap;
import java.util.Map;
/**
* @author :Mr~肖
* @date :Created in 2023/9/13 23:17
* @description: 开闭原则的基类
* @modified By:Mr~肖
* @version: 1.0
*/
public abstract class BaseOpenClose {
/**
* 抽象方法
*/
public abstract void display();
public static void main(String[] args) {
//模拟接口
//调用默认方法
printDisPlay("default");
System.out.println("---------------分割线---------------");
//调用新方法
printDisPlay("new");
}
private static void printDisPlay(String key) {
//定义两个实现类
BaseOpenClose defaultOpen = new DefaultOpen();
BaseOpenClose newOpen = new NewOpen();
//定义一个map进行存放数据
Map<String,BaseOpenClose> map = new HashMap<>(4);
map.put("default",defaultOpen);
map.put("new",newOpen);
//根据不同的选择调用不同的类
map.get(key).display();
}
}
默认实现类
package com.xiao.principles.opendemo.impl;
import com.xiao.principles.opendemo.BaseOpenClose;
/**
* @author :Mr~肖
* @date :Created in 2023/9/13 23:22
* @description:默认实现类
* @modified By:Mr~肖
* @version: 1.0
*/
public class DefaultOpen extends BaseOpenClose {
@Override
public void display() {
System.out.println("我是默认的方法");
}
}
新的实现类
package com.xiao.principles.opendemo.impl;
import com.xiao.principles.opendemo.BaseOpenClose;
/**
* @author :Mr~肖
* @date :Created in 2023/9/13 23:23
* @description:新的扩展出来的方法
* @modified By:Mr~肖
* @version: 1.0
*/
public class NewOpen extends BaseOpenClose {
@Override
public void display() {
System.out.println("我是扩展出来的方法");
}
}
输出结果
我是默认的方法
---------------分割线---------------
我是扩展出来的方法
以上模拟了一个调用,我在这里将每一个方法都放到map集合内部根据新的需求调用不同的方法,如果还需要新增类,则在继承抽象类BaseOpenClose即可,而无需在修改原来的抽象类与实现类
二、里氏代换原则
里氏代换原则是面向对象设计的基本原则之一。
里氏代换原则:任何基类可以出现的地方,子类一定也可以出现。即子类扩展父类的功能,但不能改变父类原有的功能,子类在继承父类时,除了添加新的方法完成新增功能外,尽量不重写父类原有的方法。
如果通过重写父类的方法来完成新的功能,这样写起来简单,但是整个继承体系就变得非常差,特别是多态使用频繁的时候,程序大概率会出现问题。
测试
package com.xiao.principles.richterdemo.before;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author :Mr~肖
* @date :Created in 2023/9/14 22:12
* @description:长方形类---父类
* @modified By:Mr~肖
* @version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BeforeRectangle {
/**
* 定义长方形的长
*/
private int length;
/**
* 定义长方形的宽
*/
private int width;
}
package com.xiao.principles.richterdemo.before;
/**
* @author :Mr~肖
* @date :Created in 2023/9/14 22:14
* @description:正方形
* @modified By:Mr~肖
* @version: 1.0
*/
public class BeforeSquare extends BeforeRectangle {
/**
* 将长赋值给正方形的宽高
* @param length 长
*/
@Override
public void setLength(int length) {
super.setLength(length);
super.setWidth(length);
}
/**
* 将宽赋值给正方形的宽高
* @param width 宽
*/
@Override
public void setWidth(int width) {
super.setLength(width);
super.setWidth(width);
}
}
package com.xiao.principles.richterdemo.before;
/**
* @author :Mr~肖
* @date :Created in 2023/9/14 0:06
* @description:里氏代换原则基类
* @modified By:Mr~肖
* @version: 1.0
*/
public class RichterTest {
public static void main(String[] args) {
BeforeRectangle rectangle = new BeforeRectangle(5, 2);
BeforeRectangle square = new BeforeSquare();
reSize(rectangle);
System.out.println("----------------");
square.setWidth(5);
reSize(square);
}
/**
* 重新设定长方形的宽
* @param b 长方形对象
*/
public static void reSize(BeforeRectangle b) {
while (b.getWidth() <= b.getLength()) {
b.setWidth(b.getWidth() + 1);
}
//打印长方形的长和宽
printLength(b);
}
/**
* 打印长方形的长和宽
* @param b 长方形对象
*/
private static void printLength(BeforeRectangle b) {
System.out.println("长:"+b.getLength());
System.out.println("宽:"+b.getWidth());
}
}
其结果为:
长:5
宽:6
----------------
可以看到正方形的数据没有打印出来(无法打印,会陷入死循环)则违背了里氏代换原则,对其修改
package com.xiao.principles.richterdemo.after;
/**
* @author :Mr~肖
* @date :Created in 2023/9/14 23:06
* @description:四边形接口-将get方法提取出来
* @modified By:Mr~肖
* @version: 1.0
*/
public interface Quadrangle {
/**
* 获取宽
* @return 返回宽
*/
int getWidth();
/**
* 获取长
* @return 返回长
*/
int getLength();
}
package com.xiao.principles.richterdemo.after;
/**
* @author :Mr~肖
* @date :Created in 2023/9/14 23:09
* @description:长方形类
* @modified By:Mr~肖
* @version: 2.0
*/
public class AfterRectangle implements Quadrangle{
/**
* 定义长方形的长
*/
private int length;
/**
* 定义长方形的宽
*/
private int width;
@Override
public int getLength() {
return length;
}
@Override
public int getWidth() {
return width;
}
public void setLength(int length) {
this.length = length;
}
public void setWidth(int width) {
this.width = width;
}
}
package com.xiao.principles.richterdemo.after;
/**
* @author :Mr~肖
* @date :Created in 2023/9/14 23:12
* @description:正方形类
* @modified By:Mr~肖
* @version: 2.0
*/
public class AfterSquare implements Quadrangle{
/**
* 边长
*/
private int side;
public int getSide() {
return side;
}
public void setSide(int side) {
this.side = side;
}
@Override
public int getWidth() {
return side;
}
@Override
public int getLength() {
return side;
}
}
package com.xiao.principles.richterdemo.after;
import com.xiao.principles.richterdemo.before.BeforeRectangle;
import com.xiao.principles.richterdemo.before.BeforeSquare;
/**
* @author :Mr~肖
* @date :Created in 2023/9/14 23:14
* @description: 修改后测试类
* @modified By:Mr~肖
* @version: 1.0
*/
public class AfterTest {
public static void main(String[] args) {
AfterRectangle a = new AfterRectangle();
a.setLength(5);
a.setWidth(2);
reSize(a);
System.out.println("----------------");
}
/**
* 重新设定长方形的宽
* @param a 长方形对象
*/
public static void reSize(AfterRectangle a) {
while (a.getWidth() <= a.getLength()) {
a.setWidth(a.getWidth() + 1);
}
//打印长方形的长和宽
printLength(a);
}
/**
* 打印长方形的长和宽
* @param a 长方形对象
*/
private static void printLength(AfterRectangle a) {
System.out.println("长:"+a.getLength());
System.out.println("宽:"+a.getWidth());
}
}
修改后的方法,此时的正方形接口就无法调用改变宽的接口了(未完成)
三、依赖倒转原则
依赖倒转:高层模块不应该依赖低层模块,两者都应该以来其抽象;抽象不应该以来细节,细节应该依赖抽象。简单来说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块之间的耦合。是开闭原则的具体实现
package com.xiao.principles.demo1.c_dependenceinversion.before;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 20:00
* @description:希捷硬盘
* @modified By:Mr~肖
* @version: 1.0
*/
public class XiJieHardDisk {
public void save(String data){
System.out.println("使用硬盘存储"+data);
}
public String getData() {
System.out.println("获取文件");
return "文件1";
}
}
package com.xiao.principles.demo1.c_dependenceinversion.before;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 20:04
* @description:金士顿内存条
* @modified By:Mr~肖
* @version: 1.0
*/
public class KingstonMemory {
public void save(){
System.out.println("使用金士顿内存条");
}
}
package com.xiao.principles.demo1.c_dependenceinversion.before;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 20:02
* @description:英特尔cpu
* @modified By:Mr~肖
* @version: 1.0
*/
public class IntelCpu {
/**
*使用Cpu运行
*/
public void run() {
System.out.println("使用英特尔cpu运行程序");
}
}
package com.xiao.principles.demo1.c_dependenceinversion.before;
import lombok.Data;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 20:06
* @description: 计算机类
* @modified By:Mr~肖
* @version: 1.0
*/
@Data
public class Computer {
/**
* cpu
*/
private IntelCpu cpu;
/**
* 内存条
*/
private KingstonMemory memory;
/**
* 硬盘
*/
private XiJieHardDisk hardDisk;
public void run() {
System.out.println("运行计算机");
hardDisk.getData();
cpu.run();
memory.save();
}
}
package com.xiao.principles.demo1.c_dependenceinversion.before;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 20:11
* @description:计算机测试类
* @modified By:Mr~肖
* @version:
*/
public class ComputerTest {
public static void main(String[] args) {
IntelCpu cpu = new IntelCpu();
KingstonMemory memory = new KingstonMemory();
XiJieHardDisk hardDisk = new XiJieHardDisk();
Computer computer = new Computer();
computer.setCpu(cpu);
computer.setMemory(memory);
computer.setHardDisk(hardDisk);
computer.run();
}
}
上面这些代码能满足条件,一旦用户要使用其他组件就需要更改代码,不满足开闭原则,因此对cpu,memory,hardDisk进行抽象化处理,以便满足开闭原则同时满足依赖倒转原则
接口
package com.xiao.principles.demo1.c_dependenceinversion.after;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:38
* @description:cpu接口所有cpu的上级接口
* @modified By:Mr~肖
* @version: 1.0
*/
public interface Cpu {
void run();
}
package com.xiao.principles.demo1.c_dependenceinversion.after;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:40
* @description:硬盘接口
* @modified By:Mr~肖
* @version: 1.0
*/
public interface HardDisk {
public void save(String data);
public String getData() ;
}
package com.xiao.principles.demo1.c_dependenceinversion.after;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:39
* @description:内存条接口
* @modified By:Mr~肖
* @version: 1.0
*/
public interface Memory {
void save();
}
实现类
package com.xiao.principles.demo1.c_dependenceinversion.after.impl;
import com.xiao.principles.demo1.c_dependenceinversion.after.Cpu;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:40
* @description:A品牌Cpu
* @modified By:Mr~肖
* @version:1.0
*/
public class ACpu implements Cpu {
@Override
public void run() {
System.out.println("CpuA开始运行");
}
}
package com.xiao.principles.demo1.c_dependenceinversion.after.impl;
import com.xiao.principles.demo1.c_dependenceinversion.after.Cpu;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:40
* @description:B品牌Cpu
* @modified By:Mr~肖
* @version:1.0
*/
public class BCpu implements Cpu {
@Override
public void run() {
System.out.println("CpuB开始运行");
}
}
package com.xiao.principles.demo1.c_dependenceinversion.after.impl;
import com.xiao.principles.demo1.c_dependenceinversion.after.HardDisk;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:44
* @description:A品牌硬盘
* @modified By:Mr~肖
* @version: 1.0
*/
public class AHardDisk implements HardDisk {
@Override
public void save(String data) {
System.out.println("使用A硬盘存储"+data);
}
@Override
public String getData() {
System.out.println("获取文件A");
return "文件A";
}
}
package com.xiao.principles.demo1.c_dependenceinversion.after.impl;
import com.xiao.principles.demo1.c_dependenceinversion.after.HardDisk;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:44
* @description:B品牌硬盘
* @modified By:Mr~肖
* @version: 1.0
*/
public class BHardDisk implements HardDisk {
@Override
public void save(String data) {
System.out.println("使用B硬盘存储"+data);
}
@Override
public String getData() {
System.out.println("获取文件B");
return "文件B";
}
}
package com.xiao.principles.demo1.c_dependenceinversion.after.impl;
import com.xiao.principles.demo1.c_dependenceinversion.after.Memory;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:42
* @description:A品牌内存条
* @modified By:Mr~肖
* @version: 1.0
*/
public class AMemory implements Memory {
@Override
public void save() {
System.out.println("A品牌内存条保存文件");
}
}
package com.xiao.principles.demo1.c_dependenceinversion.after.impl;
import com.xiao.principles.demo1.c_dependenceinversion.after.Memory;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:42
* @description:B品牌内存条
* @modified By:Mr~肖
* @version: 1.0
*/
public class BMemory implements Memory {
@Override
public void save() {
System.out.println("B品牌内存条保存文件");
}
}
计算机实体类
package com.xiao.principles.demo1.c_dependenceinversion.after.test;
import com.xiao.principles.demo1.c_dependenceinversion.after.Cpu;
import com.xiao.principles.demo1.c_dependenceinversion.after.HardDisk;
import com.xiao.principles.demo1.c_dependenceinversion.after.Memory;
import lombok.Data;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:50
* @description:计算机类1
* @modified By:Mr~肖
* @version: 1.0
*/
@Data
public class Computer1 {
/**
* cpu
*/
private Cpu cpu;
/**
* 内存条
*/
private Memory memory;
/**
* 硬盘
*/
private HardDisk hardDisk;
public void run() {
System.out.println("运行计算机");
hardDisk.getData();
cpu.run();
memory.save();
}
}
测试类
package com.xiao.principles.demo1.c_dependenceinversion.after.test;
import com.xiao.principles.demo1.c_dependenceinversion.after.Cpu;
import com.xiao.principles.demo1.c_dependenceinversion.after.HardDisk;
import com.xiao.principles.demo1.c_dependenceinversion.after.Memory;
import com.xiao.principles.demo1.c_dependenceinversion.after.impl.*;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 22:47
* @description:测试1
* @modified By:Mr~肖
* @version:
*/
public class ComputerTest1 {
public static void main(String[] args) {
Computer1 c = new Computer1();
Cpu aCpu = new ACpu();
Cpu bCpu = new BCpu();
HardDisk aHardDisk = new AHardDisk();
HardDisk bHardDisk = new BHardDisk();
Memory aMemory = new AMemory();
Memory bMemory = new BMemory();
//此时可自己组装电脑类
c.setHardDisk(aHardDisk);
c.setCpu(bCpu);
c.setMemory(bMemory);
c.run();
}
}
输出结果
运行计算机
获取文件A
CpuB开始运行
B品牌内存条保存文件
四、接口隔离原则
客户端不应该被迫依赖于它不使用的方法:一个类对另一个类的依赖应该建立在最小的接口上。
接口
package com.xiao.principles.demo1.d_segregation;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:12
* @description:b方法接口
* @modified By:Mr~肖
* @version: 1.0
*/
public interface AMethod {
void method1();
}
package com.xiao.principles.demo1.d_segregation;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:13
* @description: b方法接口
* @modified By:Mr~肖
* @version:1.0
*/
public interface BMethod {
void method2();
}
对应的品牌
package com.xiao.principles.demo1.d_segregation.impl;
import com.xiao.principles.demo1.d_segregation.AMethod;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:14
* @description:测试
* @modified By:Mr~肖
* @version: 1.0
*/
public class AMethodImpl implements AMethod {
@Override
public void method1() {
System.out.println("执行方法1");
}
}
package com.xiao.principles.demo1.d_segregation.impl;
import com.xiao.principles.demo1.d_segregation.AMethod;
import com.xiao.principles.demo1.d_segregation.BMethod;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:14
* @description:测试
* @modified By:Mr~肖
* @version: 1.0
*/
public class BMethodImpl implements BMethod {
@Override
public void method2() {
System.out.println("执行方法2");
}
}
package com.xiao.principles.demo1.d_segregation.impl;
import com.xiao.principles.demo1.d_segregation.AMethod;
import com.xiao.principles.demo1.d_segregation.BMethod;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:14
* @description:测试
* @modified By:Mr~肖
* @version: 1.0
*/
public class CMethodImpl implements AMethod , BMethod {
@Override
public void method1() {
System.out.println("执行方法1");
}
@Override
public void method2() {
System.out.println("执行方法2");
}
}
测试
package com.xiao.principles.demo1.d_segregation.impl;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:17
* @description:
* @modified By:Mr~肖
* @version:
*/
public class MethodTest {
public static void main(String[] args) {
AMethodImpl aMethod = new AMethodImpl();
aMethod.method1();
BMethodImpl bMethod = new BMethodImpl();
bMethod.method2();
CMethodImpl cMethod = new CMethodImpl();
cMethod.method1();
cMethod.method2();
}
}
如果还需要新的品牌则新增一个实现类即可实现,满足了开闭原则也满足了接口隔离原则
五、迪米特法则
迪米特原则又称最少知识原则。只和你的“朋友”交谈,不和“陌生人”说话
含义:如果俩个软件实体无需直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。目的是降低类之间的耦合度,提高模块的相对独立性。
迪米特法则中朋友指:当前对象本身、当前对象成员对象、当前对象所建的对象、当前对象的方法参数,这些对象存在关联、聚合或者组合关系,可以直接访问这些对象的方法。
明星类,粉丝类,公司类三个没有连接的类
package com.xiao.principles.demo1.e_lawdemeter;
import lombok.Data;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:33
* @description:明星类
* @modified By:Mr~肖粉丝
* @version: 1.0
*/
@Data
public class Star {
private String name;
}
package com.xiao.principles.demo1.e_lawdemeter;
import lombok.Data;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:34
* @description:粉丝类
* @modified By:Mr~肖
* @version: 1.0
*/
@Data
public class Fans {
private String fansName;
}
package com.xiao.principles.demo1.e_lawdemeter;
import lombok.Data;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:37
* @description:公司类
* @modified By:Mr~肖
* @version: 1.0
*/
@Data
public class Company {
private String name;
}
聚合这三个类的类
package com.xiao.principles.demo1.e_lawdemeter;
import lombok.Data;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:35
* @description:经纪人
* @modified By:Mr~肖
* @version: 1.0
*/
@Data
public class Agent {
private Star star;
private Fans fans;
private Company company;
public void meeting(){
System.out.println(fans.getFansName()+"见面"+star.getName());
}
public void business(){
System.out.println(company.getName()+"与"+star.getName()+"商讨业务");
}
}
测试类
package com.xiao.principles.demo1.e_lawdemeter;
/**
* @author :Mr~肖
* @date :Created in 2023/9/17 23:40
* @description:测试类
* @modified By:Mr~肖
* @version: 1.0
*/
public class StarTest {
public static void main(String[] args) {
Agent agent = new Agent();
Star star = new Star();
star.setName("芭芭拉");
Fans fans = new Fans();
fans.setFansName("可莉");
Company company = new Company();
company.setName("西风大教堂");
agent.setStar(star);
agent.setCompany(company);
agent.setFans(fans);
agent.meeting();
agent.business();
}
}
六、合成复用原则
合成复用原则:尽量使用组合或者聚合等关联关系来实现,其次在考虑用继承关系实现
通常类的复用分为继承复用和合成复用两种。
继承复用虽然又简单和易实现的优点,但它也存在不少缺点:
1.继承破坏了类的封装性。即父类对于子类来说是透明的
2.子类与父类的耦合度极高。父类的任何变化都会导致子类变化,不利于维护与扩展
3.限制了复用的灵活性。从父类继承的实现是静态的在编译时就定义,不能发生新的变化
组合/聚合复用,可以将已有对象纳入新对象内部,使其成为新对象的一部分,新对象可以调用已有对象的功能。优点有:
1.它维持了类的封装性。因为成员对象的内部细节不能不发现
2.对象之间的耦合的低,可以在类的成员位置声明抽象。
3.复用灵活性高。这种复用可以在运行是动态进行,新对象可以动态引用与成员对象类型相同的对象。