设计模式学习笔记之七大原则

设计模式的七大原则

  • 开闭原则(Open Closed Principle,OCP)
  • 单一职责原则(Single Responsibility Principle, SRP)
  • 里氏代换原则(Liskov Substitution Principle,LSP)
  • 依赖倒转原则(Dependency Inversion Principle,DIP)
  • 接口隔离原则(Interface Segregation Principle,ISP)
  • 合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
  • 最少知识原则(Least Knowledge Principle,LKP)或者迪米特法则(Law of Demeter,LOD)

一、开闭原则

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。模块应尽量在不修改原(是"原",指原来的代码)代码的情况下进行扩展。

开闭原则的意义

软件需要发生变更的时候,尽量减少变更的范围。
对于BUG修改尽量减小影响范围。对于功能扩展,尽量不影响原来的逻辑。
对于使用这个类的代码逻辑,也不要轻易动它。

JAVA中的接口/抽象类与实现类、扩展类之间的关系体现了这一原则。
具体实现都放在实现类或者子类里面,这样需要扩展的时候,多定义一个实现类就可以了,而不用改变原来的逻辑。
接口/抽象类体现了对修改关闭,使用者只能关心接口中定义的方法。这就对接口定义有了更高的要求。不能轻易去动它。

public class BarChart {

	public void draw(){
		System.out.println("Draw bar chart...");
	}
}

public class LineChart {

	public void draw(){
		System.out.println("Draw line chart...");
	}
}

public class App {

	public void drawChart(String type){
		if (type.equalsIgnoreCase("line")){
			new LineChart().draw();
		}else if (type.equalsIgnoreCase("bar")){
			new BarChart().draw();
		}
	}
}

修改后

public abstract class AbstractChart {

	public abstract void draw();
}

public class BarChart extends AbstractChart{

	@Override
	public void draw() {
		System.out.println("Draw bar chart...");
	}
}

public class LineChart extends AbstractChart {

	@Override
	public void draw() {
		System.out.println("Draw line chart...");
	}
}

public class App {
    /// 如果再扩展一个Chart,这部分的逻辑是完全不用动的。
    /// 但是生成AbstractChart chart的逻辑,要同步扩展,这就可能要用到工厂模式。
	public void drawChart(AbstractChart chart){
		chart.draw();
	}
}

二、单一职责原则

一个类或者模块应该有且只有一个改变的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起了。
一个类不能做太多的东西。在软件系统中,一个类(一个模块、或者一个方法)承担的职责越多,那么其被复用的可能性就会越低。
其实有的时候很难去衡量一个类的职责,主要是很难确定职责的粒度。这一点不仅仅体现在一个类或者一个模块中,也体现在采用微服务的分布式系统中。这也就是为什么我们在实施微服务拆分的时候经常会撕逼:"这个功能不应该发在A服务中,它不做这个领域的东西,应该放在B服务中"诸如此类的争论。存在争论是合理的,不过最好不要不了了之,而应该按照领域定义好每个服务的职责(职责的粒度最好找业务和架构专家咨询),得出相对合理的职责分配。
例:

public class Service {
	///S ervice做了太多东西,包括数据库连接的管理,
    ///Sql的执行这些业务层不应该接触到的逻辑,更可怕的是,例如到时候如果数据库换成了Oracle,这个方法将会大改。
	public UserDTO findUser(String name){
		Connection connection = getConnection();
		PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM t_user WHERE name = ?");
		preparedStatement.setObject(1, name);
        User user = //处理结果
        UserDTO dto = new UserDTO();
        //entity值拷贝到dto
        return dto;
	}
}

修改后:

public class Service {

    private Dao dao;
	
	public UserDTO findUser(String name){
       User user =  dao.findUserByName(name);
       UserDTO dto = new UserDTO();
        //entity值拷贝到dto
       return dto;
    }
}


public class Dao{

    public User findUserByName(String name){
       Connection connection = DataBaseUtils.getConnnection();
       PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM t_user WHERE name = ?");
		preparedStatement.setObject(1, name);
        User user = //处理结果
        return user;
    }
}

可以反过来思考,我们这段逻辑里,哪些逻辑是可能被复现,主要的逻辑链条是核心的。
以此为标准去思考设计是否合理

组合复用原则

接口和抽象类的区别,从这个角度上看,接口更像是组合,而抽象类更像是继承。
接口代表一种能力,一种属性。多个接口的实现类组合到一起,就是一个组合。
而抽象类更像是继承。它的实现类,功能独立,复用了父类的实现。

posted @ 2024-11-16 10:56  知末  阅读(7)  评论(0编辑  收藏  举报