文章目录
开闭原则
例子
其中AbstractSkin
是抽象类,皮肤类:
package com.linghu.demo01;
/**
* 抽象皮肤类
* @author linghu
* @date 2024/2/1 15:42
*/
public abstract class AbstractSkin {
//展示皮肤的方法
public abstract void display();
}
另外的DefaultSpecificSkin
、HeimaSpecificSkin
、SouGouInput
三个是子类,不动这三个子类的情况下,自己新建皮肤类,然后在下面代码【2】处进行更改,实现良好的扩展性。
Test
类如下:
package com.linghu.demo01;
/**
* @author linghu
* @date 2024/2/1 15:58
*/
public class Client {
public static void main(String[] args) {
//1、创建搜狗输入法对象
SouGouInput input = new SouGouInput();
//2、创建皮肤对象
HeimaSpecificSkin skin = new HeimaSpecificSkin();
// DefaultSpecificSkin skin = new DefaultSpecificSkin();
//3、将皮肤设置到输入法中
input.setSkin(skin);
//4、显示皮肤
input.display();
}
}
总结
对扩展开放,对修改关闭的含义就是: 我们可以在定义扩展其他皮肤类【2】,只要在上述代码中新建对象即可。不需要修改原有的DefaultSpecificSkin
、HeimaSpecificSkin
、SouGouInput
三个子类。
里氏代换原则
基本介绍
例子
为遵循里氏替换原则的后果:
package com.weirdo.principle.liskov;
public class Liskov {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3="+a.func1(11,3));
System.err.println("==========================");
B b = new B();
//这里本意求11-3
System.out.println("11-3="+b.func1(11,3));
System.out.println("1-8="+b.func1(1,8));
System.out.println("11+3+1="+b.func2(11,3));
}
}
//A类
class A {
//返回两个数的差
public int func1(int num1, int num2) {
return num1 - num2;
}
}
//B类继承了A
//增加了一个新功能:完成两个数相加,然后和1求和
class B extends A {
//这里,重写了A类的方法,可能是无意识
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return a + b + 1;
}
}
依赖倒转原则
基本介绍
例子
如下图所示,扩充的IntelCpu
类没法更换成其他的CPU类,就很不方便
在这个Computer
类中,如果我们要添加一个CPU的话,就要修改Computer
类,这就会导致臃肿麻烦。违背了开闭原则!
接下来使用依赖倒转原则,就是把类抽取出接口:
如上做的好处就是,如果有其他品牌的CPU加入,我们只需要在实现类旁边进行扩充,去实现接口即可!
接口隔离原则
基本介绍
例子
上面的代码设计如下图:
这种设计的缺陷在于,黑马品牌的安全门具有防盗,防水,防火的功能。现在如果我们还需要再创建一个传智品牌的安全门,而该安全门只具有防盗、防水功能呢?很显然如果实现SafetyDoor接口就违背了接口隔离原则,那么我们如何进行修改呢?看如下类图:
改进以后的代码:
package com.linghu.demo04.after;
/**
* 新品牌门的加入
* @author linghu
* @date 2024/2/4 9:57
*/
public class NewDoor implements AntiTheft,Fireproof{
@Override
public void antiTheft() {
System.out.println("防盗");
}
@Override
public void fireproof() {
System.out.println("防火");
}
}
新品牌的门的加入就不会在影响之前的门的牌子类了。这就是接口隔离的好处。
迪米特法则
基本介绍
为什么要找软件公司呢?这个外包公司可以帮助程序员和客户省去交流的成本,沟通的成本,提高效率!
例子
package com.linghu.demo05;
/**
* @author linghu
* @date 2024/2/4 11:12
*/
public class Client {
public static void main(String[] args) {
//创建经纪人类
Agent agent = new Agent();
//创建明星类
Star star = new Star("李小龙");
agent.setStar(star);
//创建粉丝类
Fans fans = new Fans("令狐");
agent.setFans(fans);
//创建公司对象
Company company = new Company("好莱坞");
agent.setCompany(company);
//开会和洽谈业务~
agent.meeting();
agent.business();
}
}
合成复用原则
基本介绍
例子
创建者模式
基本介绍
创建型模式分为:
- 单例模式
- 工厂方法模式
- 抽象工程模式
- 原型模式
- 建造者模式
单例设计模式
饿汉式-方法1:静态变量
package com.linghu.demo06.pattern;
/**
* 饿汉式
* 静态变量-创建类对象
* @author linghu
* @date 2024/2/4 14:41
*/
public class Singleton {
//1、私有化构造器,防止外部创建该类对象,只能让外部通过get方法访问本类
private Singleton(){
}
//2、在成员位置创建该类对象
private static Singleton instance=new Singleton();
//3、对外提供静态方法获取该类对象
public static Singleton getInstance(){
return instance;
}
}
饿汉式-方法2:静态代码块
这个方法跟上面的方法其实差不多。
package com.linghu.demo06.pattern;
/**
* 饿汉式
* 静态变量-创建类对象
* @author linghu
* @date 2024/2/4 14:41
*/
public class Singleton {
//1、私有化构造器,防止外部创建该类对象,只能让外部通过get方法访问本类
private Singleton(){
}
//2、在成员位置创建该类对象
private static Singleton instance;//null
static {
instance=new Singleton();
}
//3、对外提供静态方法获取该类对象
public static Singleton getInstance(){
return instance;
}
}
总结
懒汉式-方式1(线程不安全)
这个方式就是用饿汉式的第一种方式进行调整就可以了:
public class Singleton {
//1、私有化构造器,防止外部创建该类对象,只能让外部通过get方法访问本类
private Singleton(){
}
//2、在成员位置创建该类对象
private static Singleton instance;
//3、对外提供静态方法获取该类对象
public static Singleton getInstance(){
//instance=new Singleton();//在这里做了调整,使用该对象的时候再调用,就不会浪费了
if (instance==null){
instance=new Singleton();
}
return instance;
}
}
如上的代码中,我们加入了一个if
判断,这里的操作叫做 懒加载!
懒汉式-方式2(线程安全)
在上面懒加载的情况下加入一个 synchronized
同步锁就可以了:
package com.linghu.demo06.pattern01;
/**
* @author linghu
* @date 2024/2/4 17:01
*/
public class Singleton {
//1、构造器私有化
private Singleton(){
}
//2、创建类对象
private static Singleton instance;
//3、对外提供静态方法获取该对象
public static synchronized Singleton getInstance(){
if (instance==null){//当线程1进入以后,先判断,线程2在方法外面等待
//等待到线程1对象创建或者返回完毕释放锁以后,线程2才开始进入
instance=new Singleton();
}
return instance;
}
}
懒汉式-方式3(双重检查锁)
package com.linghu.demo07;
/**
* 双重检查方式
* @author linghu
* @date 2024/2/5 10:33
*/
public class Singleton {
//1、私有构造器
private Singleton(){}
private static Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance(){
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
if (instance==null){
synchronized (Singleton.class){
//第二次判断,抢到锁以后是否为null
if (instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
懒汉式-方式4(静态内部类)
package com.linghu.demo08;
/**
* @author linghu
* @date 2024/2/5 11:21
*/
public class Singleton {
//1、私有化构造器
private Singleton(){}
//2、静态内部类提供实例
private static class SingletonHolder{//这个类只能内部用,外界访问不了
//调用getInstance方法的时候才能初始化INSTANCE,而且只会被调用一次
private static final Singleton INSTANCE=new Singleton();
}
//3、对外提供静态方法获取该对象
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。
枚举方式-饿汉式(不考虑内存空间)
package com.linghu.demo09;
/**
* 枚举方式
* @author linghu
* @date 2024/2/5 11:34
*/
public enum Singleton {
INSTANCE;
}
JDK源码解析-RunTime类的单例设计模式
如上可以看出代码中提供了私有化构造器、静态访问对象的方法、静态变量对象:
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
工厂模式
引入工厂模式的目的是为了:解耦!
三种工厂:
- 简单工厂模式(不属于GOF的23种经典设计模式)
- 工厂方法模式
- 抽象工厂模式
引例
package com.linghu.demo10;
/**
* @author linghu
* @date 2024/2/5 16:20
*/
public abstract class Coffee {
//获取咖啡的名字,定义成抽象方法
public abstract String getName();
public void addMilk(){
System.out.println("加奶~");
}
public void addSugar(){
System.out.println("加糖~");
}
}
package com.linghu.demo10;
/**
* @author linghu
* @date 2024/2/5 16:31
*/
public class CoffeeStore {
//根据不同的咖啡类型生成并返回不同的咖啡对象
public Coffee orderCoffee(String type){
Coffee coffee=null;
if ("america".equals(type)){
coffee=new AmericaCoffee();
}else if ("latte".equals(type)){
coffee=new LatteCoffee();
}
//添加配料
coffee.addMilk();
coffee.addSugar();
return coffee;
}
}
package com.linghu.demo10;
/**
* 美式咖啡
* @author linghu
* @date 2024/2/5 16:24
*/
public class AmericaCoffee extends Coffee{
@Override
public String getName() {
return "美式咖啡";
}
}
package com.linghu.demo10;
/**
* @author linghu
* @date 2024/2/5 16:29
*/
public class LatteCoffee extends Coffee{
@Override
public String getName() {
return "拿铁咖啡";
}
}
package com.linghu.demo10;
/**
* @author linghu
* @date 2024/2/5 16:38
*/
public class Client {
public static void main(String[] args) {
//1、创建咖啡店类
CoffeeStore store=new CoffeeStore();
//2、点咖啡
Coffee orderCoffee = store.orderCoffee("america");
System.out.println(orderCoffee.getName());
}
}
简单工厂模式
结构
简单工厂模式包含结构如下:
- 抽象产品:定义了产品的规范,描述了产品的主要特征和功能。
- 具体产品:实现或者继承抽象产品的子类。
- 具体工厂:提供创建产品的方法,调用者通过该方法获取产品。
实现
对上述咖啡案例的改进就在于: 要将咖啡店和咖啡产品进行解耦工作,引入工厂模式。
工厂类代码如下:
package com.linghu.demo10;
/**
* 这个工厂就负责制造咖啡,里面没有咖啡产品,你要替换咖啡
* 产品,不需要动这里的代码,这就实现了咖啡店和咖啡产品的解耦!
* 咖啡店只需要调用这个工厂就可以生产咖啡了,不需要关注咖啡产品了!
* @author linghu
* @date 2024/2/5 17:11
*/
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type){
Coffee coffee=null;
if ("americano".equals(type)){
coffee=new AmericaCoffee();
}else if ("latte".equals(type)){
coffee=new LatteCoffee();
}
return coffee;
}
}
法,调用者通过该方法获取产品。
实现
对上述咖啡案例的改进就在于: 要将咖啡店和咖啡产品进行解耦工作,引入工厂模式。
[外链图片转存中…(img-EbAXEhcI-1711960898263)]
工厂类代码如下:
package com.linghu.demo10;
/**
* 这个工厂就负责制造咖啡,里面没有咖啡产品,你要替换咖啡
* 产品,不需要动这里的代码,这就实现了咖啡店和咖啡产品的解耦!
* 咖啡店只需要调用这个工厂就可以生产咖啡了,不需要关注咖啡产品了!
* @author linghu
* @date 2024/2/5 17:11
*/
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type){
Coffee coffee=null;
if ("americano".equals(type)){
coffee=new AmericaCoffee();
}else if ("latte".equals(type)){
coffee=new LatteCoffee();
}
return coffee;
}
}