为什么要学习设计模式
- 设计模式已经成为软件开发人员的“标准词汇”
- 学习设计模式是个人技术能力提高的捷径
- 不用重复造轮子
学习设计模式的层次
- 基本入门级
- 基本掌握级
- 深入理解和掌握级
常见的面向对象设计原则
1、单一职责SRP(Single Responsibility Principle)
所谓单一职责原则,指的是,一个类应该仅有一个引起它变化的原因。
单一职责的好处:
- 类的复杂性降低,实现什么职责都有清晰明确的定义;
- 可读性提高,复杂性降低,那当然可读性提高了;
- 可维护性提高,可读性提高,那当然更容易维护;
- 变更引起的风险降低,变更是必不可少的,但是接口的单一职责做的好,一个接口修改只对相应的实现类有影响。
public class SrpTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.updateUserInfo(1, "userId", "userName", null);
userService.updateUserInfo(2, "userId", null, "password");
}
}
interface UserService{
/**
* @param optType 1修改用户名;2重置密码
*/
void updateUserInfo(int optType, String userId, String userName, String password);
}
class UserServiceImpl implements UserService{
@Override
public void updateUserInfo(int optType, String userId, String userName, String password) {
if(optType == 1){
// 修改用户名
System.out.println("修改用户名");
}else if(optType == 2){
// 重置密码
System.out.println("重置密码");
}
}
}
其中updateUserInfo职责有修改用户名和重置密码,如果我们需要修改重置密码逻辑,就会对updateUserInfo影响,违背单一职责原则。我们可以做如下修改:
public class SrpTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.updateUserName("userId", "userName");
userService.updatePassword("userId", "password");
}
}
interface UserService{
void updateUserName(String userId, String userName);
void updatePassword(String userId, String password);
}
class UserServiceImpl implements UserService{
@Override
public void updateUserName(String userId, String userName) {
// 修改用户名
System.out.println("修改用户名");
}
@Override
public void updatePassword(String userId, String password) {
// 修改用户名
System.out.println("重置密码");
}
}
2、开发-关闭原则OCP(Open-Closed Principle)
所谓开放-关闭原则,指的是,一个类应该对扩展开放,对修改关闭。也叫开闭原则。
3、里氏替换原则LSP(Liskov Substitution Principle)
所谓里氏替换原则,指的是,子类型必须能够替换掉它们的父类型。
子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
public class LspTest {
public static void main(String[] args) {
ClassFather classFather = new ClassFather();
System.out.println(classFather.method("hello", "world"));
ClassSon classSon = new ClassSon();
System.out.println(classSon.method("hello", "world"));
}
}
class ClassFather{
public String method(String a, String b){
return a + "+" + b;
}
}
class ClassSon extends ClassFather{
@Override
public String method(String a, String b) {
return a + "-" + b;
}
}
运行结果:
hello+world
hello-world
子类中可以增加自己特有的方法。
public class LspTest {
public static void main(String[] args) {
ClassFather classFather = new ClassFather();
System.out.println(classFather.method("hello", "world"));
ClassSon classSon = new ClassSon();
System.out.println(classSon.method("hello", "world"));
System.out.println("newMethod:"+classSon.newMethod("hello", "world"));
}
}
class ClassFather{
public String method(String a, String b){
return a + "+" + b;
}
}
class ClassSon extends ClassFather{
public String newMethod(String a, String b) {
return a + "-" + b;
}
}
运行结果:
hello+world
hello+world
newMethod:hello-world
当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
public class LspTest {
public static void main(String[] args) {
HashMap<String, Object> map = new HashMap<>();
ClassFather classFather = new ClassFather();
System.out.println(classFather.method(map));
ClassSon classSon = new ClassSon();
System.out.println(classSon.method(map));
}
}
class ClassFather{
public String method(HashMap<String, Object> map){
return "this is father's method";
}
}
class ClassSon extends ClassFather{
public String method(Map<String, Object> map) {
return "this is son's method";
}
}
运行结果:
this is father's method
this is father's method
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
public class LspTest {
public static void main(String[] args) {
ClassFather classSon = new ClassSon();
classSon.method();
}
}
abstract class ClassFather{
abstract Map<String, Object> method();
}
class ClassSon extends ClassFather{
@Override
HashMap<String, Object> method() {
System.out.println("this is son's method");
return null;
}
}
运行结果:
this is son's method
4、依赖倒置原则DIP(Dependence Inversion Principle)
所谓依赖倒置原则,指的是,要依赖于抽象,不要依赖于具体类。
面向接口编程。抽象指的是接口或抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
5、接口隔离原则ISP(Interface Segregation Principle)
所谓接口隔离原则,指的是,不应该强迫客户依赖于它们不用的方法。
怎么理解呢?通俗一点就是说接口尽量细分,把不需要的方法尽量写在2个不同的接口上。
6、最少知识原则LKP(Least KnowLedge Principle)
所谓最少知识原则,指的是,只和你的朋友谈话。最少知识原则要求我们的方法调用必须保持在一定的界限范围之内,尽量减少对象的依赖关系。