smilesnake

smilesnake 查看完整档案

南昌编辑南昌大学  |  计算机信息管理 编辑江西金磊科技  |  java工程师 编辑填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

smilesnake 收藏了文章 · 7月9日

思否开源项目推介丨Knife4j:Java MVC 框架集成 Swagger 生成 API 文档的增强工具

Knife4j

开源项目名称:Knife4j
开源项目负责人:@八一菜刀
开源项目简介:Java MVC 框架集成 Swagger 生成 API 文档的增强工具
开源项目类型:个人开源项目
GitHub 数据:2.6K Star,627 Fork
地址:https://gitee.com/xiaoym/knife4j

项目介绍

在微服务架构盛行的今天,通过集成 Swagger 生成标准的 RESTful API 文档,knife4j 通过与 Swagger 的深度结合,为开发者赋能,降本增效。

knife4j 目前主要提供的功能模块包括四个方面:

  1. knife4j-spring:结合 Springfox-swagger 项目,提供增强注解及增强功能,为 Spring 的开发者提高接口对接体验;
  2. knife4j-admin:云端 Swagger 注册管理中心,提供自动注册的机制,通过云端将 Swagger 资源综合管理起来,并且提供版本机制,通过 gateway 网关任意组合各种不同的微服务文档,并且给页面加权,让接口文档更加灵活方便
  3. knife4j-vue:提供非 Java 语言开发的静态 UI 版本,Python、.net等语言也可以方便使用
  4. knife4j-service:将现有的 Swagger 资源提供一些列的在线接口服务程序,譬如 PDF、WORD、HTML 静态文档导出
  5. knife4j-extension:为了方便开发者快速调试预览 Swagger 文档而开发的 Chrome 浏览器扩展程序

增强型 SwaggerUI,提供了更多增强功能,给开发者提供一份简洁而不简单的 API 文档。

项目自荐

区别于原 swagger-ui,它主要包含的特点:

  • 左右菜单式的文档风格展示列表,接口文档更加简洁,同时具备接口文档说明及在线调试的功能
  • 接口参数缓存,方便开发者进行接口联调测试,大大提升工作效率
  • 提供接口排序的功能,接口 Setup 化流程操作,接口对接效率提升
  • 提供导出 Markdown 格式的文档,可以快速进行静态部署或文档备份,接口说明
  • 提供静态部署接口的解决方案,整合适配当前流行的微服务架构,聚合微服务文档输出
  • i18n国际化支持,目前支持中文、英文
  • 自定义文档功能,开发者可自定义文档,同时在 Swagger 的接口文档中展示,弥补接口文档仅仅展示当前 RESTful API 文档的不足,大大丰富的了接口文档的内容。

思否推荐

写接口“容易”,写接口文档“难”,“难”在没有一个可以提升写接口文档的趁手工具,“难”在写完之后对 API 各种特性进行一个整合。比如对文档的注解、PDF文档导出、接口联调测试等。

Knife4j 作为Java MVC 框架集成 Swagger 生成 API 文档的增强工具,不仅在界面上做了左右菜单式的文档风格等优化,提供了提供增强注解、云端管理 Swagger 资源且提供版本机制、支持 PDF、WORD、HTML 静态文档导出,极大的方便 Java 程序员书写 API。

SFOSSP - 思否开源项目支持计划

该项目已入选「SFOSSP - 思否开源项目支持计划」,我们希望借助社区的资源对开源项目进行相关的宣传推广,并作为一个长期项目助力开源事业的发展,与广大开发者共建开源新生态。

有意向的开源项目负责人或团队成员,可通过邮箱提供相应的信息(开源项目地址、项目介绍、团队介绍、联系方式等),以便提升交流的效率。

联系邮箱:pr@segmentfault.com

segmentfault 思否

查看原文

smilesnake 收藏了文章 · 7月9日

Redis为什么快?你只知道单线程和基于内存?抱歉我不能给你offer...

面试场景

面试官:Redis有哪些数据类型?

我:String,List,set,zset,hash

面试官:没了?

我:哦哦哦,还有HyperLogLog,bitMap,GeoHash,BloomFilter

面试官:就这?回家等通知吧。

前言

我敢肯定,第一个回答,100%的人都能说上来,但是第二个回答能回答上来的人可能就不多了,但是这也不是我今天探讨的话题。

我就从我自己的去面试的回答思路,以及作为一个面试官他想听到的标准答案来给大家出一期,Redis基础类型的文章(系列文章),写这个的时候我还是很有心得的,不知道大家有多少人跟我最开始一样,面试官问有哪些类型,就回答出那五种就结束了,如果你是这样的可以在评论区留言,让我看看有多少人是这样的。

但是,一场面试少说都是半小时起步上不封顶,你这样一句话就回答了这么重要的五个知识点,这个结果是你想要的么?是面试官想要的么?

我再问你一个问题,你可能就懵逼了:String在Redis底层是怎么存储的?这些数据类型在Redis中是怎么存放的?Redis快的原因就只有单线程和基于内存么?

宝贝,触及知识盲区没?不慌,我以前也是这样的,我以为我背出那五种就完事了,结果被面试官安排了一波,后面我苦心修炼,总算是好了一点,现在对缓存也是非常熟悉了,你不会没事,有我嘛,乖。

正文

Redis是C语言开发的,C语言自己就有字符类型,但是Redis却没直接采用C语言的字符串类型,而是自己构建了动态字符串(SDS)的抽象类型。

就好比这样的一个命令,其实我是在Redis创建了两个SDS,一个是名为aobing的Key SDS,另一个是名为cool的Value SDS,就算是字符类型的List,也是由很多的SDS构成的Key和Value罢了。

SDS在Redis中除了用作字符串,还用作缓冲区(buffer),那到这里大家都还是有点疑惑的,C语言的字符串不好么为啥用SDS?SDS长啥样?有什么优点呢?

为此我去找到了Redis的源码,可以看到SDS值的结果大概是这样的,源码的在GitHub上是开源的大家一搜就有了。

struct sdshdr{
    int len;
    int free;
    char buf[];
}

回到最初的问题,为什么Redis用了自己新开发的SDS,而不用C语言的字符串?那好我们去看看他们的区别。

SDS与C字符串的区别

  1. 计数方式不同

C语言对字符串长度的统计,就完全来自遍历,从头遍历到末尾,直到发现空字符就停止,以此统计出字符串的长度,这样获取长度的时间复杂度来说是0(n),大概就像下面这样:

但是这样的计数方式会留下隐患,所以Redis没有采用C的字符串,我后面会提到。

而Redis我在上面已经给大家看过结构了,他自己本身就保存了长度的信息,所以我们获取长度的时间复杂度为0(1),是不是发现了Redis快的一点小细节了?还没完,不止这些。

  1. 杜绝缓冲区溢出

字符串拼接是我们经常做的操作,在C和Redis中一样,也是很常见的操作,但是问题就来了,C是不记录字符串长度的,一旦我们调用了拼接的函数,如果没有提前计算好内存,是会产生缓存区溢出的。

比如本来字符串长这样:

你现在需要在后面拼接 ,但是你没计算好内存,结果就可能这样了:

这是你要的结果么?很显然,不是,你的结果意外的被修改了,这要是放在线上的系统,这不是完了?那Redis是怎么避免这样的情况的?

我们都知道,他结构存储了当前长度,还有free未使用的长度,那简单呀,你现在做了拼接操作,我去判断一些是否可以放得下,如果长度够就直接执行,如果不够,那我就进行扩容。

这些大家在Redis源码里面都是可以看到对应的API的,后面我就不一一贴源码了,有兴趣的可以自己去看一波,需要一点C语言的基础。

  1. 减少修改字符串时带来的内存重分配次数

C语言字符串底层也是一个数组,每次创建的时候就创建一个N+1长度的字符,多的那个1,就是为了保存空字符的,这个空字符也是个坑,但是不是这个环节探讨的内容。

Redis是个高速缓存数据库,如果我们需要对字符串进行频繁的拼接和截断操作,如果我们写代码忘记了重新分配内存,就可能造成缓冲区溢出,以及内存泄露。

内存分配算法很耗时,且不说你会不会忘记重新分配内存,就算你全部记得,对于一个高速缓存数据库来说,这样的开销也是我们应该要避免的。

Redis为了避免C字符串这样的缺陷,就分别采用了两种解决方案,去达到性能最大化,空间利用最大化:

  • 空间预分配:当我们对SDS进行扩展操作的时候,Redis会为SDS分配好内存,并且根据特定的公式,分配多余的free空间,还有多余的1byte空间(这1byte也是为了存空字符),这样就可以避免我们连续执行字符串添加所带来的内存分配消耗。

    比如现在有这样的一个字符:

我们调用了拼接函数,字符串边长了,Redis还会根据算法计算出一个free值给他备用:

我们再继续拼接,你会发现,备用的free用上了,省去了这次的内存重分配:

  • 惰性空间释放:刚才提到了会预分配多余的空间,很多小伙伴会担心带来内存的泄露或者浪费,别担心,Redis大佬一样帮我们想到了,当我们执行完一个字符串缩减的操作,redis并不会马上收回我们的空间,因为可以预防你继续添加的操作,这样可以减少分配空间带来的消耗,但是当你再次操作还是没用到多余空间的时候,Redis也还是会收回对于的空间,防止内存的浪费的。

    还是一样的字符串:

当我们调用了删减的函数,并不会马上释放掉free空间:

如果我们需要继续添加这个空间就能用上了,减少了内存的重分配,如果空间不需要了,调用函数删掉就好了:

  1. 二进制安全

仔细看的仔肯定看到上面我不止一次提到了空字符也就是’0‘,C语言是判断空字符去判断一个字符的长度的,但是有很多数据结构经常会穿插空字符在中间,比如图片,音频,视频,压缩文件的二进制数据,就比如下面这个单词,他只能识别前面的 不能识别后面的字符,那对于我们开发者而言,这样的结果显然不是我们想要的对不对。

Redis就不存在这个问题了,他不是保存了字符串的长度嘛,他不判断空字符,他就判断长度对不对就好了,所以redis也经常被我们拿来保存各种二进制数据,我反正是用的很high,经常用来保存小文件的二进制。

资料参考:Redis设计与实现

总结

大家是不是发现,一个小小的SDS居然有这么多道理在这?

以前就知道Redis快,最多说个Redis是单线程的,说个多路IO复用,说个基于内存的操作就完了,现在是不是还可以展开说说了?

本文是系列文的第一章,后续会陆续更新的,不知道这样的类型大家是否喜欢,可以留言给我反馈。

大家一同去面试,一样的问题,就是有人能过,有人不能过,大家经常归咎于自己学历,自己过往经历的原因,但是你可以问一下自己,底层的细节字节是否有深究呢?细节往往才是最重要的,也是最少人知道的,如何和别的仔拉开差距拿到offer,我想就是这样些细节决定的吧,背谁不会呢?

絮叨

我是敖丙,一个在互联网苟且偷生的程序员。

你知道的越多,你不知道的越多人才们的 【三连】 就是丙丙创作的最大动力,我们下期见!

注:如果本篇博客有任何错误和建议,欢迎人才们留言!


查看原文

smilesnake 发布了文章 · 2019-11-20

自定义协议之解析字节不得不说的操作

自定义协议-----字节不得不说的操作


一、得到高4位


 /**
  * 得到高4位
  */
 public static int getHeight4(byte data){
    return ((data & 0xf0) >> 4);
 }
 

二、得到低4位


 /**
  * 得到低四位
  */
 public static int getLow4(byte data){
    return (data & 0x0f);
 }

三、得到byte某一位bit值:


/**
  * 得到byte某一位bit值
  * @param b 字节
  * @param index 位置的下标(从0开始)
  */
  public int getBit(byte b,int index) {
     int bit = (int)((b>>i) & 0x1);
     return bit;
  }     

四、获取多个连续的bit值


/**
  * 获取多个连续的bit值
  *
  * @param b 字节
  * @param start 开始的下标
  * @param len 结束的下标
  */
  public static int getBits(byte b, int start, int len) {
    return (b >> start) & (0xFF >> (8 - len));
  }

五、打印字节序(字节转16进制)


  /**
  * 字节转16进制.
  * @param bs 字节数组
  */
  private String bytesToHex(byte[] bs) {
    char[] chars = "0123456789ABCDEF".toCharArray();
    StringBuilder sb = new StringBuilder("");
    int bit;
    for (int i = 0; i < bs.length; i++) {
       bit = (bs[i] & 0x0f0) >> 4;
       sb.append(chars[bit]);
       bit = bs[i] & 0x0f;
       sb.append(chars[bit]);
       sb.append(' ');
    }
  }
查看原文

赞 0 收藏 0 评论 0

smilesnake 发布了文章 · 2019-11-02

centos 7 设置固定ip

编辑配置

$ vim /etc/sysconfig/network-script/ens-33

TYPE="Ethernet"
DEFROUTE="yes"
PEERDNS="yes"
PEERROUTES="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_PEERDNS="yes"
IPV6_PEERROUTES="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="ens33"
UUID="e22979db-f642-4e0d-b6df-f8a0fd5cc440"
DEVICE="ens33"
ONBOOT="yes"
# 要修改的部分
# 此处修改成static代表静态ip
BOOTPROTO="static"

# 新增的部分
# 自定义的静态ip
IPADDR=192.168.58.131
NETMASK=255.255.255.0
GATEWAY=192.168.58.2
DNS1=8.8.8.8
如果是克隆的机器,需要添加如下内容,MAC地址可通过ifconfig查出
HWADDR=00:0c:39:7f:c8:6c
重启服务
$ systemctl restart network

注意:可能需要重启
reboot

查看原文

赞 0 收藏 0 评论 0

smilesnake 发布了文章 · 2019-10-29

解决国内无法下载k8s镜像

k8s 架构图:
桌面1.png
桌面2.png

第一章

前期准备工作:
(1)关闭防火墙,和selinux

 yum -y install wget vim net-tools ntpdate
 systemctl stop firewalld
 systemctl disable firewalld
 sed -i 's/enforcing/disabled/' /etc/selinux/config
 setenforce 0
 systemctl stop NetworkManager
 systemctl disable NetworkManager

(2)时钟同步

 echo '*/10 * * * * /usr/sbin/ntpdate -s 10.100.60.6 >/dev/null 2>&1 && /sbin/clock -w' > /spool/cron/root 
 
 service crond restart 
 
 ntpdate -s 10.100.60.6

(3)私有主机禁用swap分区

 swapoff -a   
 [root@master01 ~]# cat /etc/fstab 
 /dev/mapper/centos-root /                       xfs          defaults        0 0
 UUID=8d103c59-0306-4493-94f2-1e3726d87cfb /boot                   xfs     defaults        0 0
 #/dev/mapper/centos-swap swap                    swap         defaults        0 0

(4)互相解析

 cat >> /etc/hosts << EOF
 192.168.187.141 master01
 192.168.187.142 master02
 192.168.187.143 node01
 192.168.187.144 node02
 192.168.187.145 node03
 EOF

(5)master对node节点ssh互信

 [root@master01 ~]# ssh-keygen
 [root@master01 ~]# ssh-copy-id node01
 [root@master01 ~]# ssh-copy-id node02

(6)修改内核参数

 cat > /etc/sysctl.d/k8s.conf << EOF
 net.bridge.bridge-nf-call-ip6tables = 1
 net.bridge.bridge-nf-call-iptables = 1
 EOF
 sysctl --system

第二章

注意:以下在所有节点执行(master+node),安装docker,kubeadm,kubelet
1、配置docker源

 cat >> /etc/yum.repos.d/docker.repo <<EOF
 [docker-repo]
 name=Docker Repository
 baseurl=http://mirrors.aliyun.com/docker-engine/yum/repo/main/centos/7
 enabled=1
 gpgcheck=0
 EOF

配置kubernetes源

 cat >> /etc/yum.repos.d/kubernetes.repo <<EOF
 [kubernetes]
 name=Kubernetes
 baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
 enabled=1
 gpgcheck=0
 EOF

 yum clean all
 yum makecache

2、安装kubeadm和相关工具包(所有节点)

 yum install -y docker --disableexcludes=docker-repo
 systemctl enable docker && systemctl start docker

 yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
 systemctl enable kubelet && systemctl start kubelet

3、初始kubeadm集群环境(仅master节点)

 kubeadm init --image-repository=registry.aliyuncs.com/google_containers --service-cidr=10.1.0.0/16 --pod-network-cidr=10.244.0.0/16 --kubernetes-version=v1.15.2

安装完成后记录一下

 Your Kubernetes control-plane has initialized successfully!

 To start using your cluster, you need to run the following as a regular user:

   mkdir -p $HOME/.kube
   sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
   sudo chown $(id -u):$(id -g) $HOME/.kube/config

 You should now deploy a pod network to the cluster.
 Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
   https://kubernetes.io/docs/concepts/cluster-administration/addons/

 Then you can join any number of worker nodes by running the following on each as root:

 kubeadm join 192.168.187.141:6443 --token wfj4bi.qmsscvrqwc816qm2 \
--discovery-token-ca-cert-hash sha256:8835f7dac11f8a8a10a0eae322b5b6b61a192da0f7d3bd7cfc0db40c4883b161 


 #执行操作:
 mkdir -p $HOME/.kube
 cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
 chown $(id -u):$(id -g) $HOME/.kube/config

 [root@master01 ~]# kubectl get nodes
 NAME       STATUS     ROLES    AGE     VERSION     
 master01   NotReady   master   2m42s   v1.15.1      #状态是Notready,在等待网络的加入

 [root@master01 ~]# kubectl get pod -n kube-system      #看到有2个pod处于pending
 NAME                               READY   STATUS         RESTARTS   AGE
 coredns-bccdc95cf-bhtms            0/1     Pending   0          4m18s
 coredns-bccdc95cf-jmbds            0/1     Pending   0          4m17s
 etcd-master01                      1/1     Running   0          3m30s
 kube-apiserver-master01            1/1     Running   0          3m15s
 kube-controller-manager-master01   1/1     Running   0          3m23s
 kube-proxy-n62h7                   1/1     Running   0          4m18s
 kube-scheduler-master01            1/1     Running   0          3m14s

4、在master节点上安装flannel网络

 kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml

 [root@master01 ~]# kubectl get pod -n kube-system    #看到所有的pod都处于running状态。
 NAME                               READY   STATUS    RESTARTS   AGE
 coredns-bccdc95cf-bhtms            1/1     Running   0          6m4s
 coredns-bccdc95cf-jmbds            1/1     Running   0          6m3s
 etcd-master01                      1/1     Running   0          5m16s
 kube-apiserver-master01            1/1     Running   0          5m1s
 kube-controller-manager-master01   1/1     Running   0          5m9s
 kube-flannel-ds-amd64-6jjwf        1/1     Running   0          59s
 kube-proxy-n62h7                   1/1     Running   0          6m4s
 kube-scheduler-master01            1/1     Running   0          5m

5、添加计算节点(在节点上执行)

 [root@node01 ~]# kubeadm join 192.168.187.141:6443 --token wfj4bi.qmsscvrqwc816qm2 \
 >     --discovery-token-ca-cert-hash sha256:8835f7dac11f8a8a10a0eae322b5b6b61a192da0f7d3bd7cfc0db40c4883b161 

 [root@node02 ~]# kubeadm join 192.168.187.141:6443 --token wfj4bi.qmsscvrqwc816qm2 \
 >     --discovery-token-ca-cert-hash sha256:8835f7dac11f8a8a10a0eae322b5b6b61a192da0f7d3bd7cfc0db40c4883b161 

 [root@master01 ~]# kubectl get nodes
 NAME       STATUS     ROLES    AGE     VERSION
 master01   Ready      master   8m55s   v1.15.2
 node01     NotReady   <none>   37s     v1.15.2
 node02     NotReady   <none>   14s     v1.15.2

6、部署dashboard(在master上操作)

 [root@master01 ~]# kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta1/aio/deploy/recommended.yaml

 [root@master01 ~]# kubectl get pods --namespace=kubernetes-dashboard      #查看创建的namespace
 NAME                                          READY   STATUS    RESTARTS   AGE
 kubernetes-dashboard-5c8f9556c4-w6pzj         1/1     Running   0          7m46s
 kubernetes-metrics-scraper-86456cdd8f-7js7v   1/1     Running   0          7m46s

 [root@master01 ~]# kubectl get service --namespace=kubernetes-dashboard   #查看端口映射关系
 NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
 dashboard-metrics-scraper   ClusterIP   10.98.83.31     <none>        8000/TCP        55m
 kubernetes-dashboard        NodePort    10.107.192.48   <none>        443:30520/TCP   55m

7、修改service配置,将type: ClusterIP改成NodePort

 [root@master01 ~]# kubectl edit service kubernetes-dashboard --namespace=kubernetes-dashboard
 如下:
 spec:
   clusterIP: 10.107.192.48
   externalTrafficPolicy: Cluster
   ports:
   - nodePort: 30520
     port: 443
     protocol: TCP
     targetPort: 8443
   selector:
     k8s-app: kubernetes-dashboard
   sessionAffinity: None
   type: NodePort      #注意这行。

8、创建dashboard admin-token(仅master上执行)

 cat >/root/admin-token.yaml<<EOF
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1beta1
 metadata:
   name: admin
   annotations:
     rbac.authorization.kubernetes.io/autoupdate: "true"
 roleRef:
   kind: ClusterRole
   name: cluster-admin
   apiGroup: rbac.authorization.k8s.io
 subjects:
 - kind: ServiceAccount
   name: admin
   namespace: kube-system
 ---
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: admin
   namespace: kube-system
   labels:
     kubernetes.io/cluster-service: "true"
     addonmanager.kubernetes.io/mode: Reconcile
 EOF

 #创建用户
 [root@master01 ~]# kubectl create -f admin-token.yaml
 clusterrolebinding.rbac.authorization.k8s.io/admin created
 serviceaccount/admin created

 #获取token
 root@master01 ~]# kubectl describe secret/$(kubectl get secret -nkube-system |grep admin|awk '{print $1}') -nkube-system
 Name:         admin-token-lzkfl
 Namespace:    kube-system
 Labels:       <none>
 Annotations:  kubernetes.io/service-account.name: admin
          kubernetes.io/service-account.uid: 6f194e13-2f09-4800-887d-99a183b3d04d

 Type:  kubernetes.io/service-account-token

 Data
 ====
 ca.crt:     1025 bytes
 namespace:  11 bytes
 token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi10b2tlbi1semtmbCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjZmMTk0ZTEzLTJmMDktNDgwMC04ODdkLTk5YTE4M2IzZDA0ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphZG1pbiJ9.vlSVy45l42tzpm2ppSJIBMSKm_OcL_f-miwOV6M37R3b3nda48FNvMQ1oTWFKRM4VZugJcrkSMg7OVBQqyZ7CVV94xaMiiV49eaDFfd9dKGmihHVAfQuywvwcA4wYnspf0TVQYmkQPOagPzPxQGzNZvulsB9LeCCvVUSnKXeW3O0U8rSL13WplBwwcjC2J91vZpljgNaBMiuemafR1kqRmdZ7CLGuaLNCLw5LGLeTi0OQKZhI15Wjhs2juIqP7ZI6qtgDsSeYe8Y3tUxD4Htqa5VtYxhZKll-5jFWgNXWE3xnzdUVCW8t4iLMoFbxrs5ar-vQOIL8C11zBAmeWdJeQ
 [root@master01 ~]# 

9.登录dashboard https://192.168.187.141:30520/#/overview?namespace=default

 选择token登录。

原文链接:https://blog.csdn.net/mtldswz...

查看原文

赞 0 收藏 0 评论 1

smilesnake 发布了文章 · 2019-08-26

日志的范围

日志的范围


日志记录器(Logger)是日志处理的核心组件。log4j具有5种正常级别(Level)。日志记录器(Logger)的可用级别Level (不包括自定义级别 
Level), 以下内容就是摘自log4j API (http://jakarta.apache.org/log4j/docs/api/index.html):
  • static Level WARN

           WARN level表明会出现潜在错误的情形。
  • static Level ERROR

    ERROR level指出虽然发生错误事件,但仍然不影响系统的继续运行。       
  • static Level FATAL

                FATAL level指出每个严重的错误事件将会导致应用程序的退出。

另外,还有两个可用的特别的日志记录级别: (以下描述来自log4j APIhttp://jakarta.apache.org/log4j/docs/api/index.html):

  • static Level ALL

           ALL Level是最低等级的,用于打开所有日志记录。
  • static Level OFF

       OFF Level是最高等级的,用于关闭所有日志记录。

日志记录器(Logger)的行为是分等级的。如下表所示:

      分为OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者您定义的级别。Log4j建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、
  INFO、DEBUG。通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关。比如在这里定义了INFO级别,则应用程序中所有
  DEBUG级别的日志信息将不被打印出来,也是说大于等于的级别的日志才输出。

      日志记录的级别有继承性,子类会记录父类的所有的日志级别。
查看原文

赞 0 收藏 0 评论 0

smilesnake 赞了回答 · 2018-10-10

支付宝当面付中的条形码支付实现原理是什么?

其实很简单,条形码就是一串数字,后台与一个待支付账单一一对应,支付宝客户端拿到这段数字之后,向后台请求这个账单,就可以向其支付了。二维码支付也是类似的。

关注 2 回答 1

smilesnake 发布了文章 · 2018-06-19

设计模式 ----- 开篇


设计模式并非类库


    为了方便地编写java程序,我们会使用类库,但设计模式不是类库。

    与类型库相比,设计模式是一个更为普遍的概念。类库是由程序组合而成的组件,而设计模式则是来表现内

部组件是如何被组装的,以及每一个组件是如何通过相互关联构成一个庞大的系统。

                                                 -- 引用自《图解设计模式》 结城浩 著

起源


     最早提出"设计模式"概念。是在1970年,由建筑设计大师亚力山大Alexander<<建筑的永恒之道>>描述:
     
模式是一条由三部分组成的通过规则:它表示了一个特定环境、一类问题和一个解决方案之间的关系。每一个 模

式描述了一个不断重复发生的问题,以及该问题解决方案的核心设计。在他的另一本书<<建筑者模式语言>>中提

到了现在已经定义的253种模式。

     尽管Alexander的著作是针对建筑领域的,但他的观点适用于所以的工程设计领域,其中也包含软件设 计

领域。"软件设计模式",这个术语是由1990年代由Erich Gamma等2 从建筑设计领域引入到计算机科学中来的。

目前主要有23种。当然,设计模式不仅仅中有23种。

     这里引用费马理论:光线传播的路径是需时最少的路径。而设计模式,在软件开发中,可以让我们少走很多

弯路,他是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。

     软件需求变幻无穷,计划没有变化快,但是我们还是要寻找出不变的东西,并将它和变化的东西分离开来,

这需要非常的智慧和经验。设计模式是在这方面开始探索的一块里程碑。    

     为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模

式是软件工程的基石脉络,如同大厦的结构一样

设计原则


#1 单一职责原则( Single Responsibility Principle )


     "一个类仅有一个职责"或者"引起类变化的只有一个原因",这就是单一职责原理

     就一个类而言,应该仅有仅有一个引起它变化的原因。简单来说,这个类应该是一组相关性很高的函数,数
     
据的封装。单一职责的划分界限并不是总那么清晰,很多的时候都是需要靠个人经验去界定。

     类只因一个类的原因去变化,面对对象编程时,试图把一个事物对象成一个类,事务这个类具备的功能都是

这个类的操作。比如。1块钱,它既可以买辣条,它也可以去买小浣熊便利面等。而在单一职责原理下,钱的功能就

是引起这个类变化的两个原因,就应该写成两个类。

     如果混在一起写,在修改的一个职责的时候,就会影响到另一个职责。当另一个类只使用其中一个职责的时
     
候,另一不会用到的职责会消耗更多的资源。

#2 开闭原则( Open Close Principle )


     "对于扩展是开放的,对于修改是关闭的",这就是开闭原则原理

     开闭原则明确的告诉我们:软件实现应该对扩展开放,对修改关闭,其含义就是说:一个软件应该通过

扩展来实现变化,而不是修改已有的代码来实现变化的。
     
     打个比方:1块钱,它既可以买2包辣条。但是由于物价上涨,现在只能买一块。那么根据开闭原则,你

并不能在原来的代码中去修改已有的代码。换句话说,现在是对已有代码的一种修改,但是,如果他现在还能

界定他还能买一根香肠。那么现在是对已经代码的一种扩展。

     如果直接修改代码,那么如果他要换回去怎么办,又去改回来?所以,开闭原则可以提高程序的复用性、

维护性。

#3 里氏代换原则( Liskov Substitution Principle )


    "任何基类可以出现的地方,子类一定可以出现。 里氏代换原则是继承复用的基石,只有当衍生类可以

替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新

的行为"。

     所有引用基类的地方必须透明的使用其子类的对象。

 上两点就是里氏代换原则。  

打个比方:如果对每一个类型为"驼鸟"的对象a,都有类型为"鸟"的对象b,使得以"鸟"定义的所有程序C在
    
所有的对象a都代换为b,程序行为没有发生变化,那么类型"驼鸟"是"鸟"的子类型。只要有"鸟"能出现的地方

"驼鸟"也可以出现,而且替换为子类不会产生任何的Error或Exception,但是,"驼鸟"出现的地 方,"鸟"未

必就能适应。

#4 依赖倒转原则( Dependence Inversion Principle )


  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象

  • 抽象不依赖实现

  • 实现依赖抽象

   以上三点就是依赖倒转的原则,什么意思呢?指的是模块与模块之间的信赖是通过抽象发生的,实现类之间
   
相互独立,之间不发生直接的信赖关系,其依赖关系是通过接口或者抽象类产生的,接口或者抽象类不依赖实现,

实现依赖接口或者抽象类。更加精简的意思叫做“面向接口编程” 。

   采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,减少并行开发引起的风险,提高代码的可读性

和可维护性。

   这里打个比方:人,和一包笑笑牌辣条。创建一个人类A,里面有一个吃辣条的动作a1方法,再来一个笑笑

牌辣条类B,里面有一个打印辣条自己被吃的方法b1方法,再来一个终端,通过A.a1调用B.b1,这里我们可以发

现,这个人吃辣条这个场景,人类A和笑笑牌辣条类B之间是一个紧耦合的关系,假如我们这里还有一个只吃花生

的人,怎么办?如果这里把这个动作抽象化,定义一个接口C,接口C中有一个吃的方法,然后创建一个花生类,

里面有一个自己被吃的打印方法c1。以后如果还有别的功能,那么实现接口。就可以了。同理,如果这里人分为

大人和小人,那么就可以定义一个抽象类(人),然后再定义一个两个类大人和小孩,继承这个类。这样一来,

我们这个场景就可以减少类间的耦合性,提高系统稳定性,减少并行开发引起的风险,提高代码的可读性和可维

护性以为可扩展性。

#5 接口隔离原则( InterfaceSegregation Principles )


  • 客户端不应该依赖它不需要的接口

  • 一个类对另一个类的依赖应该建立在最小的接口上

   这里的意思是建立单一的接口,接口尽量细化,如果有不需要的接口,客户端需要什么接口,就给什么接 

口,把不需要的接口剔除了。---     

接口隔离原则与单一职责原则有什么区别


    接口隔离原则与单一职现原则其实也没有,主要是角度不一样,单一职责原则要求类和接口只应该响应一

个变化,接口隔离原则要求接口的方法尽可能少,尽可能的去细化

    总之呢:接口要尽量小,接口要高内聚,接口设计是有限度的。一个接口只服务一地一个子模块或者业务。

这个要划分开了,不要糅合在一起。接口要不断的精简,使之更加完善。如果接口是坏的,改之。如果背锅的

可能性大。就采用适配器去处理。尽量不要去背锅,这个会很难受。

#6 迪米特原则 ( Law of Demeter )


    这个也叫最少知识原则(Low knowledge Principle)。最早是在1987年由美国Northeastern 

University的Ian Holland提出。类与类之间的关系越好(紧密),耦合度越高(大),当一个类发生改变时,

对另一个类的影响也越大。于是就提出了迪米特法则。通俗的来讲,就是一个类对自己依赖的类知道的越少越

好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供

的public方法,不对外泄漏任何信息。

    打个比方,有一个辣条类,老板叫员工去清算辣条的包数。这里我们创建一个辣条类A,创建一个员工类B,

里面有一个接收辣条列表参数功能为清算辣条数量的方法b1。然后我们一个老板类C,里面有一个功能为命令

员工清算辣条的方法c1。里面初始化了辣条的数量。我们通过调用这个命令来达到清算辣条的数量。这里是我

们经常做的,没有不一样,但是我们可以发现,这里老板居然去初始化辣条。这里就有违反了迪米特原则。它

破坏了老板类的健壮性。我们只需要在老板类中写一个命令的方法就可以了,只调用员工清算的方法就可以了

,让main方法(客户端)去创建辣条。这样就保证了类与类之间的健壮性。

    总之核心观念就是类间解耦,弱耦合,只有弱耦合后,类的复用率才可以提高。其结果就是产生了大量的

中转或跳转类,导致系统复杂,为维护带来了难度。所以,我们在实践时要反复权衡,即要让结构清晰,又做

到高内聚低耦合。   

  设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的 

  设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了
  
  可重用代码、让代码更容易被他人理解、保证代码可靠性             

参考

几种常见的设计模式

如果有侵权,马上删除

查看原文

赞 0 收藏 0 评论 0

smilesnake 发布了文章 · 2018-06-12

mybatis 为什么每次插入的时候总会创建一个SqlSession?

问题记录:


     工作环境是使用spring boot,使用用的mybatis,在一次调试中。发现每一次插入一条
 数据都会创建一个SqlSession。如图:

图1:

clipboard.png

问题可能的原因:


原因分析:#1 没有使用缓存

因为这个是插入,不是查询,所以这里不存在什么缓存的问题。

后来百度了一波,网上说是没有使用事务。

加上@Transactional


图2:

clipboard.png

发现“Creating a new SqlSession”这两个烦人的东西居然还在。

不管了,直接分析源码

直接分析源码,老子还不信了,搞不定你我还混什么:
1.开启debug
2.打上断点

图3:

clipboard.png

发现session为空

继续走...

clipboard.png

图2 #分析


public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
     //从从前线程的threadLocal 中获取sqlSessionHolder
     SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
     //调用静态方法sessionHoler 判断是否存在符合要求的sqlSession
     SqlSession session = sessionHolder(executorType, holder);
     // 判断当前sqlSessionHolder 中是否持有sqlSession (即当前操作是否在事务当中)
     if (session != null) {
         //如果持有sqlSesison 的引用,则直接获取
         return session;
     }

     if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Creating a new SqlSession");
     }
    //获取新的sqlSession 对象。这里由sessionFacory产生的defaultSqlSession
     session = sessionFactory.openSession(executorType);
    //判断判断,当前是否存在事务,将sqlSession 绑定到sqlSessionHolder 中,并放到threadLoacl 当中
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
    return session;
}

图3 #分析

private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
     SqlSession session = null;
     if (holder != null && holder.isSynchronizedWithTransaction()) {
     //hodler保存的执行类型和获取SqlSession的执行类型不一致,就会抛出异常,也就是说在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用 
         if (holder.getExecutorType() != executorType) {
             throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
         }
             //增加该holder,也就是同一事务中同一个sqlSessionFactory创建的唯一sqlSession,其引用数增加,被使用的次数增加 
             holder.requested();

         if (LOGGER.isDebugEnabled()) {
             LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
         }
         //返回sqlSession 
         session = holder.getSqlSession();
    }
    return session;
}

当然,这里还少了一个注册的方法,贴上:

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
        SqlSessionHolder holder; 
        //判断事务是否存在
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            Environment environment =  sessionFactory.getConfiguration().getEnvironment();
            //加载环境变量,判断注册的事务管理器是否是SpringManagedTransaction,也就是Spring管理事务
        if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
             if (LOGGER.isDebugEnabled()) {
                 LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
              }

             holder = new SqlSessionHolder(session, executorType, exceptionTranslator); 
             //如果当前回话处在事务当中,则将holder 绑定到ThreadLocal 中
             //以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources中 
              TransactionSynchronizationManager.bindResource(sessionFactory, holder);
              //将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations 
              TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
              //设置当前holder和当前事务同步 
              holder.setSynchronizedWithTransaction(true);
              //holder 引用次数+1
             holder.requested();
          } else {
                if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
                    }
                } else {
                       throw new TransientDataAccessResourceException( "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
                }
          }
} else {
  if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
  }
}

}

补充


  • sqlSessionHolder 继承了spring的ResourceHolderSupport

    public abstract class ResourceHolderSupport implements ResourceHolder {
       //事务是否开启private boolean synchronizedWithTransaction = false;    
       private boolean rollbackOnly = false;
       private Date deadline;
       // 引用次数
       private int referenceCount = 0;
       private boolean isVoid = false;
    }

  • 在sqlSession 关闭session 的时候, 使用了工具了sqlSessionUtils的closeSqlSession 方法。sqlSessionHolder 也是做了判断,如果回话在事务当中,则减少引用次数,没有真实关闭session。如果回话不存在事务,则直接关闭session

    public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
         notNull(session, NO_SQL_SESSION_SPECIFIED);
         notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    
         SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
         //如果holder 中持有sqlSession 的引用,(即会话存在事务)
         if ((holder != null) && (holder.getSqlSession() == session)) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
            } 
           //每当一个sqlSession 执行完毕,则减少holder 持有引用的次数
           holder.released();
           } else {
                  if (LOGGER.isDebugEnabled()) {
                      LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
                  }
                  //如果回话中,不存在事务,则直接关闭session
                  session.close();
            }
    }

    到了这一步,问题已经很明显了。


出现原因 与 分析


  • SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); 这一句:他从从前线程的threadLocal 中获取sqlSessionHolder。但是在在sqlSession 关闭session 的时候,sqlSessionHolder也是做了判断。如果会话在事务中,就减少引用次数,没有真实关闭session。如果会话不存在事务,则直接关闭session。也就是说,必须开启事务,但这个问题好像只是插入了一下,事务已经执行完成了,下一次插入的时候,由于上一个事务执行完成了, 如果不存在holder或没有被事务锁定,则会创建新的sqlSession,即 Creating a new SqlSession,通过sessionFactory.openSession()方法。如果会话不存在事务,就直接把session关闭了,同时,也减少了引用次数。

  • 换一句话来说:如果在插入的代码块中,再加上一个查询的代码,或者再插入一条数据的代码,这样就不会出现Creating a new SqlSession这个烦人的家伙。好了,祝大家好运!!!

引用:

SqlSessionHolder作用
如果有侵权,马上删除

查看原文

赞 4 收藏 4 评论 3

smilesnake 发布了文章 · 2018-06-03

设计模式 ----- 开篇


设计模式并非类库


    为了方便地编写java程序,我们会使用类库,但设计模式不是类库。

    与类型库相比,设计模式是一个更为普遍的概念。类库是由程序组合而成的组件,而设计模式则是来表现内

部组件是如何被组装的,以及每一个组件是如何通过相互关联构成一个庞大的系统。

                                                 -- 引用自《图解设计模式》 结城浩 著

起源


     最早提出"设计模式"概念。是在1970年,由建筑设计大师亚力山大Alexander<<建筑的永恒之道>>描述:
     
模式是一条由三部分组成的通过规则:它表示了一个特定环境、一类问题和一个解决方案之间的关系。每一个 模

式描述了一个不断重复发生的问题,以及该问题解决方案的核心设计。在他的另一本书<<建筑者模式语言>>中提

到了现在已经定义的253种模式。

     尽管Alexander的著作是针对建筑领域的,但他的观点适用于所以的工程设计领域,其中也包含软件设 计

领域。"软件设计模式",这个术语是由1990年代由Erich Gamma等2 从建筑设计领域引入到计算机科学中来的。

目前主要有23种。当然,设计模式不仅仅中有23种。

     这里引用费马理论:光线传播的路径是需时最少的路径。而设计模式,在软件开发中,可以让我们少走很多

弯路,他是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。

     软件需求变幻无穷,计划没有变化快,但是我们还是要寻找出不变的东西,并将它和变化的东西分离开来,

这需要非常的智慧和经验。设计模式是在这方面开始探索的一块里程碑。    

     为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模

式是软件工程的基石脉络,如同大厦的结构一样

设计原则


#1 单一职责原则( Single Responsibility Principle )


     "一个类仅有一个职责"或者"引起类变化的只有一个原因",这就是单一职责原理

     就一个类而言,应该仅有仅有一个引起它变化的原因。简单来说,这个类应该是一组相关性很高的函数,数
     
据的封装。单一职责的划分界限并不是总那么清晰,很多的时候都是需要靠个人经验去界定。

     类只因一个类的原因去变化,面对对象编程时,试图把一个事物对象成一个类,事务这个类具备的功能都是

这个类的操作。比如。1块钱,它既可以买辣条,它也可以去买小浣熊便利面等。而在单一职责原理下,钱的功能就

是引起这个类变化的两个原因,就应该写成两个类。

     如果混在一起写,在修改的一个职责的时候,就会影响到另一个职责。当另一个类只使用其中一个职责的时
     
候,另一不会用到的职责会消耗更多的资源。

#2 开闭原则( Open Close Principle )


     "对于扩展是开放的,对于修改是关闭的",这就是开闭原则原理

     开闭原则明确的告诉我们:软件实现应该对扩展开放,对修改关闭,其含义就是说:一个软件应该通过

扩展来实现变化,而不是修改已有的代码来实现变化的。
     
     打个比方:1块钱,它既可以买2包辣条。但是由于物价上涨,现在只能买一块。那么根据开闭原则,你

并不能在原来的代码中去修改已有的代码。换句话说,现在是对已有代码的一种修改,但是,如果他现在还能

界定他还能买一根香肠。那么现在是对已经代码的一种扩展。

     如果直接修改代码,那么如果他要换回去怎么办,又去改回来?所以,开闭原则可以提高程序的复用性、

维护性。

#3 里氏代换原则( Liskov Substitution Principle )


    "任何基类可以出现的地方,子类一定可以出现。 里氏代换原则是继承复用的基石,只有当衍生类可以

替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新

的行为"。

     所有引用基类的地方必须透明的使用其子类的对象。

 上两点就是里氏代换原则。  

打个比方:如果对每一个类型为"驼鸟"的对象a,都有类型为"鸟"的对象b,使得以"鸟"定义的所有程序C在
    
所有的对象a都代换为b,程序行为没有发生变化,那么类型"驼鸟"是"鸟"的子类型。只要有"鸟"能出现的地方

"驼鸟"也可以出现,而且替换为子类不会产生任何的Error或Exception,但是,"驼鸟"出现的地 方,"鸟"未

必就能适应。

#4 依赖倒转原则( Dependence Inversion Principle )


  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象

  • 抽象不依赖实现

  • 实现依赖抽象

   以上三点就是依赖倒转的原则,什么意思呢?指的是模块与模块之间的信赖是通过抽象发生的,实现类之间
   
相互独立,之间不发生直接的信赖关系,其依赖关系是通过接口或者抽象类产生的,接口或者抽象类不依赖实现,

实现依赖接口或者抽象类。更加精简的意思叫做“面向接口编程” 。

   采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,减少并行开发引起的风险,提高代码的可读性

和可维护性。

   这里打个比方:人,和一包笑笑牌辣条。创建一个人类A,里面有一个吃辣条的动作a1方法,再来一个笑笑

牌辣条类B,里面有一个打印辣条自己被吃的方法b1方法,再来一个终端,通过A.a1调用B.b1,这里我们可以发

现,这个人吃辣条这个场景,人类A和笑笑牌辣条类B之间是一个紧耦合的关系,假如我们这里还有一个只吃花生

的人,怎么办?如果这里把这个动作抽象化,定义一个接口C,接口C中有一个吃的方法,然后创建一个花生类,

里面有一个自己被吃的打印方法c1。以后如果还有别的功能,那么实现接口。就可以了。同理,如果这里人分为

大人和小人,那么就可以定义一个抽象类(人),然后再定义一个两个类大人和小孩,继承这个类。这样一来,

我们这个场景就可以减少类间的耦合性,提高系统稳定性,减少并行开发引起的风险,提高代码的可读性和可维

护性以为可扩展性。

#5 接口隔离原则( InterfaceSegregation Principles )


  • 客户端不应该依赖它不需要的接口

  • 一个类对另一个类的依赖应该建立在最小的接口上

   这里的意思是建立单一的接口,接口尽量细化,如果有不需要的接口,客户端需要什么接口,就给什么接 

口,把不需要的接口剔除了。---     

接口隔离原则与单一职责原则有什么区别


    接口隔离原则与单一职现原则其实也没有,主要是角度不一样,单一职责原则要求类和接口只应该响应一

个变化,接口隔离原则要求接口的方法尽可能少,尽可能的去细化

    总之呢:接口要尽量小,接口要高内聚,接口设计是有限度的。一个接口只服务一地一个子模块或者业务。

这个要划分开了,不要糅合在一起。接口要不断的精简,使之更加完善。如果接口是坏的,改之。如果背锅的

可能性大。就采用适配器去处理。尽量不要去背锅,这个会很难受。

#6 迪米特原则 ( Law of Demeter )


    这个也叫最少知识原则(Low knowledge Principle)。最早是在1987年由美国Northeastern 

University的Ian Holland提出。类与类之间的关系越好(紧密),耦合度越高(大),当一个类发生改变时,

对另一个类的影响也越大。于是就提出了迪米特法则。通俗的来讲,就是一个类对自己依赖的类知道的越少越

好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供

的public方法,不对外泄漏任何信息。

    打个比方,有一个辣条类,老板叫员工去清算辣条的包数。这里我们创建一个辣条类A,创建一个员工类B,

里面有一个接收辣条列表参数功能为清算辣条数量的方法b1。然后我们一个老板类C,里面有一个功能为命令

员工清算辣条的方法c1。里面初始化了辣条的数量。我们通过调用这个命令来达到清算辣条的数量。这里是我

们经常做的,没有不一样,但是我们可以发现,这里老板居然去初始化辣条。这里就有违反了迪米特原则。它

破坏了老板类的健壮性。我们只需要在老板类中写一个命令的方法就可以了,只调用员工清算的方法就可以了

,让main方法(客户端)去创建辣条。这样就保证了类与类之间的健壮性。

    总之核心观念就是类间解耦,弱耦合,只有弱耦合后,类的复用率才可以提高。其结果就是产生了大量的

中转或跳转类,导致系统复杂,为维护带来了难度。所以,我们在实践时要反复权衡,即要让结构清晰,又做

到高内聚低耦合。   

  设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的 

  设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了
  
  可重用代码、让代码更容易被他人理解、保证代码可靠性             

参考

几种常见的设计模式

如果有侵权,马上删除

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 54 次点赞
  • 获得 14 枚徽章 获得 0 枚金徽章, 获得 2 枚银徽章, 获得 12 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-01-21
个人主页被 645 人浏览