设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式具有以下优点
- 可以提高程序员的思维能力、编程能力和设计能力
- 使程序设计更加标准、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期
- 使设计的代码可复用性更高、可读性强、可靠性高、灵活性好、可维护性强
设计模式的七大原则(OOP) 抽象
1、开闭原则(Open Close Principle)
开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。
2、里氏代换原则(Liskov Substitution Principle)
继承必须确保超类所拥有的性质在子类中依然成立
任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒置原则(Dependence Inversion Principle)
要面向接口编程,不要面向实现编程
针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
要为各个类建立他们需要的专用接口
使用多个隔离的接口,比使用单个接口要好。另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
只与你的直接朋友交谈,不跟“陌生人”说话
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。高内聚低耦合
6、合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承
7、单一职责原则
控制类的粒度大小、将对象解耦、提高其内聚性
一天一种设计模式(二)工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
它定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
作用:实现了创建者和调用者的分离
满足OOP七大原则之开闭原则、依赖倒转原则、迪米特法则
核心本质:
实例化对象不用new,用工厂方法代替
将选择实现类、创建对象统一管理和控制,从而将调用者和我们的实现类解耦
三种模式
1、简单工厂模式(静态工厂模式)
用来生产同一等级结构中的任意产品(以下面例子为例,车即为统一等级结构,特斯拉和五菱即这一等级结构下的产品)
创建一个Car接口表示产品的等级结构
public interface Car {
void carName();
}
创建产品类,每个产品都是独立的
public class WuLing implements Car{
@Override
public void carName() {
System.out.println("五菱欢迎你!");
}
}
public class Tesla implements Car{
@Override
public void carName() {
System.out.println("特斯拉等着你!");
}
}
创建汽车工厂类表示所有的汽车产品都在这里生产
public class CarFactory {
public Car getCar(Car car){
car = null;
if (car.equals("五菱")){
car = new WuLing();
}else if(car.equals("特斯拉")){
car = new Tesla();
}
return car;
}
}
消费者购买车的时候则只需要选择汽车的品牌就行(即在工厂里自己选择而不用跑到车间去自己生产)
public class Consumer {
public static void main(String[] args) {
/*new 表示自己创建对象(即到车间自己生产)
Car consumer1 = new WuLing();
Car consumer2 = new Tesla();
consumer1.carName();
consumer2.carName();
*/
Car consumer1 = CarFactory.getCar("五菱");
Car consumer2 = CarFactory.getCar("特斯拉");
consumer1.carName();
consumer2.carName();
}
}
简单工厂存在的问题: 简单工厂模式有一个问题就是,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则。比如:
我们还需要生产“奔驰”的车,首先我们要创建奔驰这个车的实体类
public class BenChi implements Car {
@Override
public void carName() {
System.out.println("奔驰很贵!");
}
}
但是这个时候我们也需要对工厂类进行修改,如下所示
public class CarFactory {
public static Car getCar(String car){
Car vehicle = null;
if (car.equals("五菱")){
vehicle = new WuLing();
}else if(car.equals("特斯拉")){
vehicle = new Tesla();
}else if(car.equals("奔驰")){
vehicle = new BenChi();
}
return vehicle;
}
}
由此,引出工厂方法模式。
2、工厂方法模式
用来生产同一等级结构中的固定产品(支持增加任意产品)
相对于简单工厂方法,工厂方法模式增加了各个品牌的4S店,在这种设计模式下,车工厂也被设置为接口,然后增加了各个品牌的4S店(即WuLingFactory、TeslaFactory等),这种情况下,客户直接去喜欢的品牌的4S店里购车而不用再和车工厂产生交互。在这种模式下,当车工厂需要增加新产品时就可以重新开一个该品牌的4S店(即新建一个XXXFactory类)而不用和其他品牌产生关系。
public interface CarFactory {
Car getCar();
}
public class WuLingFactory implements CarFactory{
@Override
public Car getCar() {
return new WuLing();
}
}
public class Consumer {
public static void main(String[] args) {
Car consumer1 = new WuLingFactory().getCar();
Car consumer2 = new TeslaFactory().getCar();
consumer1.carName();
consumer2.carName();
}
}
其实这个模式的好处就是,如果你现在想增加一个功能,只需做一个实现类就OK了,无需去改动现成的代码。这样做,拓展性较好。
由上面简单工厂模式和工厂方法模式我们可以看出,客户在简单工厂模式是在工厂提的车,在工厂方法模式是在4S店提的车(即工厂发生变化,用户也要跟着变化,这无疑就增加了用户的操作复杂性。),那我们能不能送货上门呢?由此引出下面的抽象工厂模式
这里和直接new WuLing()的区别:前面说了设计模式的本质是面向对象设计原则的实际运用,所以这里在很简单的几行代码看来创建工厂是多此一举,但是从本质上来说它实现了开闭原则和依赖倒置原则以及高内聚,这对于后期的维护和升级有着很重要的作用。更重要的是思维的提升
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
根据设计原则:工厂方法模式
根据实际业务:简单工厂模式:虽然某种程度上不符合设计原则,但实际使用的最多
3、抽象工厂模式
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂
抽象工厂模式也是独立的一个设计模式,我们在下面一篇博客再讲。