网站 只做程序员,做公司网站的公,如何编辑网站模板,工作服厂家联系方式在软件开发和软件架构领域#xff0c;深厚的理论基础是构建高质量、可维护、可扩展系统的关键#xff0c;本部分内容将围绕这些基础理论展开。#xff08;本部分内容介绍第一部分#xff1a;编程三范式、架构设计原则、软件设计七原则#xff09;
一、编程三范式
编程范… 在软件开发和软件架构领域深厚的理论基础是构建高质量、可维护、可扩展系统的关键本部分内容将围绕这些基础理论展开。本部分内容介绍第一部分编程三范式、架构设计原则、软件设计七原则
一、编程三范式
编程范式是程序设计的基本思想和方法对于程序员来说他是告诉我们在实际编码和设计的过程中不能做什么而非可以做什么。了解和灵活运用编程范式有助于构建更加清晰、灵活、可维护的代码。
1 、结构化编程Structured programming
是基于过程的一种编程范式将问题分解为一系列的步骤每个步骤都由一个过程或函数表示。强调程序的执行过程是用于简单的任务和流程。
结构化编程是对程序控制权的直接转移进行了限制和规范。
优势
直观且易于理解和实现适用于线性、简单的任务
适用场景
独立脚本编写简单的操作和流程
2、面向对象编程Object-oriented programming
是一种以对象为基础的编程范式将数据和操作封装在对象中强调对象之间的交互和关系。
核心思想是通过抽象、继承、封装和多态等概念提高代码的重用性和可维护性。
面向对象编程是对程序控制权的间接转移进行了限制和规范。
优势
模块化提高代码的可维护性重用性高减少重复代码
适用场景
复杂系统设计大规模团队协作
3、函数式编程Funtional programming
是一种以函数为基础的编程范式将计算视为数据函数的求值过程。强调纯函数、不可变性和无副作用通过组合函数来构建复杂的系统。
函数式编程是对程序中的赋值进行了限制和规范。
优势
副作用少易于调试和测试并行计算友好
适用场景
数据处理和转换并行和分布式系统
了解并灵活运用这三个范式有助于根据问题的性质选择最合适的编程方式提高代码的质量和可维护性。在实际项目中通常会综合运用多种范式以充分发挥他们的优势。
4、编程范式与架构设计的关系
编程范式和架构设计是有着密切关系的
结构化编程是在做各个模块设计和实现时的基础要求面向对象编程中比如多态是跨越架构边界的手段函数式编程是用来规范和限制数据存放位置与访问权限的手段
基于上面的分析你会发现这三点和架构设计中的功能性、组件独立性、数据管理是密切相关的。
二、架构设计原则
1 、 合适原则
架构设计应该全面考虑时间、人力、能力、效率、成本选择当前最为合适的方案。
合适的方案并非简单化而是在充分考虑投资回报率ROI的基础上根据项目需求和实际情况进行权衡和决策。
在实现功能的前提下追求高效率和降低成本是合适方案的关键。
2 、简单原则
简单原则强调在满足功能需求的前提下追求简洁和可读性。
简单设计有助于提高研发效率、降低成本、减少错误率并能够提高系统的可维护性和可扩展性。
核心思想是简单优于复杂。在实现功能的前提下减少代码量和复杂性避免过度设计带来的复杂度。
简单的设计更容易理解、测试和部署同时也易于维护和扩展减少未来的开发难度和成本。
3 、演化原则
演化原则是指导设计出能够适应未来变化的重要原则要求技术人员具备前瞻性。
架构设计应该具备灵活性和可扩展性以便在未来的开发过程中能轻松应对新功能的研发、适应新的需求和变化。
核心思想是演化强于一步到位。任何一个软件产品和系统都需要不断变化和演进因此在设计过程中要避免过于复杂、难以维护和扩展的结构。
持续的演化有助于保持系统的健康状态适应业务和技术的不断发展。
三、软件设计七原则
做好一个系统要先从写出整洁和易维护的代码开始。如果代码质量不佳那架构能起到的作用就不会太大多好的架构也绷不住堆砌的烂代码。反之如果架构设计的不太理想代码写的再好其实也无太大用处。两者之间是相辅相成互相成就的。
1 、SRPSingle responsibility Principle单一职责原则
SRP 是一项简单易懂且重要的原则但是在实际过程中往往最容易忽略的它强调在应用中一个类应该只有一个引起变化的原因只有一个职责。
SRP 是基于康威定律的推导结论软件系统的最佳结构高度依赖于开发这个系统的组织的内部结构每个软件模块都有且只有一个需要被改变的理由。
优点
降低类之间的耦合将不同职责分解为不同的类降低类之间的依赖关系提高系统的灵活性和可维护性提升类的可维护性和可重用性当一个类只有一个职责时修改该职责不会影响到其他职责使得类更加稳定易于维护和重用简化设计过程SRP 原则使得类的设计更加清晰每个类都专注于解决一个问题降低了设计的复杂性
示例代码
多职责的类设计
public class UserService {/*** 运营部员工绩效计算 */public BigDecimal calculateKPIResultForOperation() { }/*** 商务员工绩效计算 */public BigDecimal calculateKPIResultForBusiness() { }/*** 产品部员工绩效计算 */public BigDecimal calculateKPIResultForProduct() { }/*** 技术部员工绩效计算 */public BigDecimal calculateKPIResultForTechnology() { }
}
SRP 后的类设计
public interface UserService {public BigDecimal calculateKPIResult();
}// 不同部门实现接口
public class OperationUserService implement UserService {}
public class BusinessUserService implement UserService {}
public class ProductUserService implement UserService {}
public class TechnologyUserService implement UserService {}注意点
拆分多职责类如果一个类承担了多个职责就一定要拆分为多个类确保每个类只有一个职责识别变化原因识别引起类变化的原因然后职责分配到对应的类确保每个类的变化原因是明确的避免过度设计尽可能简单避免过度设计和复杂性SRP 的目标是简化设计而不是增加不必要的结构
2 、OCPOpen/Close Principle 开闭原则
OCP 的核心思想是通过扩展已有的代码增加新的行为和功能而不是修改已有的代码。
优点
提高代码的可维护性和可重用性允许系统通过扩展已有的代码来应对新的需求而不是修改已有的代码降低引入错误的风险减少代码的耦合度模块化的设计使得系统个部分之间的耦合度降低一个模块的修改不会对其他模块产生影响提高系统的可扩展性和可升级性新功能可以通过添加新的类或模块来实现而不是修改已有的代码使得系统更容易扩展和升级
示例代码
无 OCP 的代码
public class Calculator { public int add(int a, int b) { return a b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; }
}有 OCP 的代码
public interface Operation { int calculate(int a, int b);
} public class AddOperation implements Operation { public int calculate(int a, int b) { return a b; }
} public class SubtractOperation implements Operation { public int calculate(int a, int b) { return a - b; }
} public class MultiplyOperation implements Operation { public int calculate(int a, int b) { return a * b; }
} public class DivideOperation implements Operation { public int calculate(int a, int b) { return a / b; }
}
注意点
使用 OCP 原则把实体设计成可扩展通过新加类、模块或者函数来扩展功能和行为抽象化已有代码对已有代码进行抽象将具体的实现细节封装对外仅提供抽象的接口避免过度设计OCP 原则的目标是简化设计而不是增加不必要的结构
3 、LSPLiskov Substitution Principle里氏替换原则
LSP 强调在软件中子类必须能够替换其父类即子类应该具有与父类相同的行为和功能而不仅仅是继承父类的属性和方法。
优点
提高代码的可读性和可维护性子类与父类具有一致的行为和功能使得代码更易于理解和维护减少代码的冗余和复杂性子类继承父类的方法和属性可以避免重复编写相似的代码提高系统的可扩展性和可升级性新的子类可以无缝地替换父类不会影响系统的其他部分
示例代码
无 LSP 代码
class Shape { void draw() { System.out.println(Drawing a shape); }
} class Circle extends Shape { void draw() { System.out.println(Drawing a circle); }
}
有 LSP 代码
interface Drawable { void draw();
} class Shape implements Drawable { public void draw() { System.out.println( Drawing a shape); }
} class Circle extends Shape { public void draw() { System.out.println(Drawing a circle); }
}
注意点
子类具有一致的行为和功能子类必须具有与父类相同的行为和功能但不能改变父类的行为和功能抽象类定义抽象方法抽象类应该定义抽象方法具体方法在子类中实现避免继承滥用避免使用继承来共享行为继承是用来实现多态行为的而不是为了代码的重用。如果子类需要不同的功能和行为那应该通过重写父类方法来实现
4 、ISPInterface Segregation Principle接口隔离原则
ISP 强调在应用中使用多个特定的接口而不是一个单一的总接口从而避免端侧就不需要被强制依赖他们不需要的接口。
优点
提高代码的可维护性和可重用性特定接口提供特定服务代码可以更加模块化和可定制化减少端侧的复杂性端侧只需要依赖实际使用的接口避免对不相关接口的强制依赖提高系统的可扩展性和可升级性新的接口可以被添加而不会影响实际使用的接口使得系统更容易扩展和升级
示例代码
无 ISP 代码
interface ShoppingCart { void addItem(Product product, int quantity); void removeItem(Product product); void updateQuantity(Product product, int quantity);
} class ShoppingCartImpl implements ShoppingCart { private MapProduct, Integer items new HashMap(); Override public void addItem(Product product, int quantity) { items.put(product, quantity); } Override public void removeItem(Product product) { items.remove(product); } Override public void updateQuantity(Product product, int quantity) { int currentQuantity items.get(product); items.put(product, currentQuantity quantity); }
}
有 ISP 代码
interface AddToCart { void addItem(Product product, int quantity);
} interface RemoveFromCart { void removeItem(Product product);
} interface UpdateQuantity { void updateQuantity(Product product, int quantity);
} class ShoppingCartImpl implements AddToCart, RemoveFromCart, UpdateQuantity { private MapProduct, Integer items new HashMap(); Override public void addItem(Product product, int quantity) { items.put(product, quantity); } Override public void removeItem(Product product) { items.remove(product); } Override public void updateQuantity(Product product, int quantity) { int currentQuantity items.get(product); items.put(product, currentQuantity quantity); }
}
注意点
接口定义尽可能小每个接口提供有限的服务方法尽可能少不要妄想一个接口走遍天下分离不相关功能如果接口中提供的功能不相关需要将接口进行分离操作形成独立接口代码可更模块化和可定制化避免使用过大的总接口总接口应该根据需要提供适当的功能而不是一刀切提供所有功能
5 、DIPDependency Inversion Principle依赖倒置原则
DIP 强调在应用中高层模块不应该依赖于底层模块它们应该依赖于抽象。
优点
提高代码的可读性和可维护性高层模块依赖于抽象而不是具体实现使得代码更灵活和易于理解降低类之间的耦合度依赖抽象不依赖具体实现减少了高层模块和底层模块之间的直接依赖提高了系统的灵活性提高系统的可扩展性和可升级性新的实现可以通过实现抽象来引入不需要修改高层模块的代码
示例代码
无 DIP 代码
class UserService { private UserDao userDao; public UserService(UserDao userDao) { this.userDao userDao; } public User getUserById(int userId) { return userDao.getUserById(userId); }
} class UserDao { public User getUserById(int userId) { // 具体实现逻辑如从数据库中获取用户信息 return new User(userId, John Doe); }
}
有 DIP 代码
interface UserDataAccess { User getUserById(int userId);
} class UserDao implements UserDataAccess { Override public User getUserById(int userId) { // 具体实现逻辑如从数据库中获取用户信息 return new User(userId, John Doe); }
} class UserService { private UserDataAccess userDataAccess; public UserService(UserDataAccess userDataAccess) { this.userDataAccess userDataAccess; } public User getUserById(int userId) { return userDataAccess.getUserById(userId); }
}
注意点
通过接口或抽象类定义依赖关系使用接口或抽象类来定义高层模块和底层模块之间的依赖关系避免直接依赖具体类如果直接依赖具体类一旦有修改依赖元就要同步改动影响和成本都较高使用依赖注入解耦使用依赖注入来解耦类之间的依赖关系通过注入抽象的实现来实现高层模块对底层模块的依赖
6 、CARPComposition/Aggregation Reuse Principle合成/聚合复用原则
CARP 强调在应用设计过程中优先使用合成/聚合的关系而不是继承的关系来实现复用。
优点
更好地代码封装通过使用合成/聚合可以将对象的不同部分封装在不同的类中更好地隐藏细节提高代码的模块化和可维护性更灵活的代码结构通过使用合成/聚合可以更容易地改变对象的行为和结构只需要修改相关的类不需要修改整个继承体系更好的可重用性通过使用合成/聚合可以根据需要组合不同的对象来实现代码的可重用性且合成/聚合本身是一种松耦合可以更便捷地组装新的对象类型更好的可扩展性通过使用合成/聚合更便捷地添加新的功能和行为到应用中这种灵活的关系可以更容易适应新的需求和变化
示例代码
无 CARP 代码
class Car { private Engine engine; private Transmission transmission; private Wheel wheel; private Door door; public Car(Engine engine, Transmission transmission, Wheel wheel, Door door) { this.engine engine; this.transmission transmission; this.wheel wheel; this.door door; } public void start() { engine.start(); } public void shift(int gear) { transmission.shift(gear); } public void turn(int degrees) { wheel.turn(degrees); } public void open() { door.open(); }
}
有 CARP 代码
interface Engine { void start();
}
interface Transmission { void shift(int gear);
}
interface Wheel { // 可以添加一些方法例如 rotate() 和 brake() 等
}
interface Door { void open();
}
class Car { private Engine engine; private Transmission transmission; private Wheel wheel; private Door door; public Car(Engine engine, Transmission transmission, Wheel wheel, Door door) { this.engine engine; this.transmission transmission; this.wheel wheel; this.door door; }
}
注意点
封装变化将变化的部分封装起来使得变化对其他部分的影响最小强调组合/聚合首选使用组合/聚合关系而不是直接继承关系以提高灵活性和可维护性松耦合合成/聚合是一种松耦合关系允许系统更容易地适应变化
7 、LoDLaw of Demeter迪米特法则
LoD 强调在应用中应该尽量减少对象之间的直接依赖关系降低耦合度提高可维护性和可重用性。
核心思想是一个对象对其他对象保持最少的了解并且只和那些和自己最有直接关系的对象进行交互。
一个对象只暴露必要的接口给其他对象并且应该通过这些接口与其他对象进行交互。
优点
降低耦合度减少对象之间的直接依赖关系使得系统更容易扩展和维护提高可维护性对象之间的松耦合关系使得修改一个对象的内部实现不会影响其他对象提高可重用性松耦合关系允许对象更容易地被独立重用在不同的上下文中
示例代码
无 LoD 代码
class Account { private User user; private ListTransaction transactions; public Account(User user, ListTransaction transactions) { this.user user; this.transactions transactions; } public double getBalance() { double balance 0.0; for (Transaction transaction : transactions) { balance transaction.getAmount(); } return balance; } public void debit(double amount) { user.setBalance(user.getBalance() - amount); }
}
有 LoD 代码
interface UserService { double getBalance(User user);
} interface TransactionService { void debit(User user, double amount);
} class Account { private UserService userService; private TransactionService transactionService; public Account(UserService userService, TransactionService transactionService) { this.userService userService; this.transactionService transactionService; } public double getBalance() { return userService.getBalance(userService.getUser()); } public void debit(double amount) { transactionService.debit(userService.getUser(), amount); }
}
注意点
定义接口将对象的相关操作定义在接口中而不是直接依赖于具体的实现通过接口交互对象应该通过接口进行交互而不是直接调用其他对象的方法减少依赖关系一个对象应该只与其直接的朋友发生交互避免依赖过多的对象