java基础巩固-宇宙第一AiYWM:入门java--设计模式,a story +设计模式的总结

PART1:a story
有一天,FHAM(FamilyHuAIMu)游戏团队队员小言说:接到一个活,需要咱们设计一款“鸭子群”游戏,大家开始讨论…

小胡:最简单的思路就是设计一个鸭子父类(鸭子祖类,父类中有红色,绿色,蓝色,铁鸭子,泥巴等属性,然后有游泳,吃喝拉撒睡等方法),然后然接下来的一群不同的鸭子分别继承这个父类,从而构造出我们自己的鸭子群(会飞的蓝鸭子,只会游泳不会飞的红鸭子等等)。大家便开始干并交付给用户。

(旁白)大家想一想这样一来会不会出现什么问题呢
过了一周…

小言:用户嫌弃咱们鸭子群的,反馈回来说:我想让我的鸭子飞上天,与太阳肩并肩…,要不直接给父类鸭子加上飞翔方法吧。行呀,交给你了,干掉他。

小胡:因为用的继承嘛,给那个鸭子父类加上飞方法后,鸭子们不久全都会飞了。不过,后来发现铁鸭子,泥巴鸭子也会飞了…由于违反常识,可能会带坏小朋友,咱们的鸭子群游戏被封杀了。

(旁白)所以得出一个结论,不能滥用继承

又过了一周,游戏团队队员小言觉得不甘心,说:“那不用继承了,咱们可以把鸭子父类中的飞翔呀,吃饭呀,睡觉呀,学习呀等等方法分别都封装为独立的接口,然后让之前的鸭子子类们谁需要什么方法,直接去父类中实现重写就行了呗,这总不会出错吧。

(旁白)分开代码中常变化和不经常变化的部分—鸭子游戏,那就先分为两组呗

小胡:你意思是不是这样,把飞方法单独拿出来,放在一个飞接口中,然后算上鸭子父类,就有一个飞接口,一个鸭子父类。让鸭子群中谁想飞谁就来实现这个飞接口,并且重写接口中的飞方法。然后后期鸭子群中哪个子鸭子还想有父类鸭子中的哪个行为,咱就把父类鸭子中的那个方法继续拿出来放到一个接口中,让那个想要这个行为的子鸭子来实现这个接口并重写接口中的方法就行了。

小言:可以倒可以,不过要是这样干,那代码会不会有点浪费呢,一个类中只有一个方法

小胡:是可以有属性的,你想呀,最起码不谈另外的属性,就飞翔这个方法本身。你看飞翔的频率呀,翅膀的大小呀,飞翔的距离呀等等,都可以算作属性,用在类中呀,这不是目前用不到嘛。

小胡:对了,你看看我前几天看到的一个东西

public abstract class Duck{//这是鸭子父类
    Fly f1;

    public Duck(){}//鸭子父类的构造器

    public void perform_fly(){
        f1.fly//fly是上面那个飞接口中那个飞方法       
    }
}

public interface Fly{
    public void fly();
}

public class Fly1 implemrnts Fly{
    public void fly(){
    ......
    }
}

public class DuckSon1 extends Duck{
    public DuckSon1(){//这是鸭子子类1的构造器
        f1 = new Fly1();
    }
}

小言:看这样子,调用这个perform_fly方法就相当于调用那个飞接口的实现类中的那个飞翔方法了呀,这真是巧妙呀。

小胡:嗯,每次看到这个代码,就想吟诗一首:掬水月在手,弄花香满衣。这句我一直也挺喜欢的。

小言:而且,好像那个Fly就是咱们独立分离出来的含有飞方法的接口呀,而那个Fly1就是咱们Fly接口的一个实现类吧。

小胡:bingo…聪明呀,小伙子可以呀,偷走了我的脑纸吧你。

小胡:怕惊艳到你,来,再看

 public abstract class Duck{//这是鸭子父类 Fly f1;
    public Duck(){}//鸭子父类的构造器

    public void perform_fly(){
        f1.fly//fly是上面那个飞接口中那个飞方法       
    }

    public void setFly(Fly f){
        f2 = f;
    }
}

小言:等等,你这好像是在父类鸭子中增加了一个setFly方法,然后就可以调用此方法,将那个接口传进来后,是不是就可以动态改变接口的实现类的那个飞行为了呢,好灵活呀。
小言:是不是这样

public class Test{
    public static void main(){
        Duck du = new DuckSon1()
        du.perform_fly();

        du.setFly(new DuckSon2)

        du.perform_fly();
    }
}

public class DuckSon2 extends Duck{
    public DuckSon2(){//这是鸭子子类1的构造器
        f2 = new Fly1();
    }

}

小言:哦,你这就是相当于把鸭子子类1的飞行为改成了鸭子子类2的行为了呀,张冠李戴,那你两次du.perform_fly();出来的结果肯定不一样吧。

小胡:那肯定的喽。

小言:打哈欠…困啦,那收工吧,今天到此为止,明天再见。

PART2:设计模式

  • 说起设计模式,肯定都会首先想到,单例模式、代理模式以及策略模式等等,但是呢,我感觉K8S中文社区中一篇文章中的一段话很有道理:模式就是经验,设计模式就是设计经验,有了这些经验,我们就能 在特定情况下使用特定的设计、组合设计,这样可以大大节省我们的设计时间,提高工作效率。总体而言,共有八种:
    • 单库单应用模式:
      在这里插入图片描述
    • 内容分发模式:
      在这里插入图片描述
    • 查询分离模式:
      在这里插入图片描述
      • 具体业务需求场景:
        • 场景一:全文关键词检索
          • 如果使用传统的数据库技术,大部分可能都会使用like这种SQL语句,高级一点可能是先分词,然后通过分词index相关的记录SQL语句的性能问题与全表扫描机制导致了非常严重的性能问题,现在基本上很少见到。这里的ES是ElasticSearch的缩写,是一种查询引擎,类似的还有Solr等,都差不多的技术,ES较Solr配置简单、使用方便,所以这里选用了它。另外,ES支持横向扩展,理论上没有性能的瓶颈。同时,还支持各种插件、自定义分词器等,可扩展性较强。在这里,使用ES不仅可以替代数据库完成全文检索功能,还可以实现诸如分页、排序、分组、分面等功能
            • 一般的流程是这样的:
              • 服务端把一条业务数据落库,并且服务端异步把该条数据发送到ES
              • ES把该条记录按照规则、配置放入自己的索引库
              • 客户端查询的时候,由服务端把这个请求发送到ES,得到数据后,根据需求拼装、组合数据,返回给客户端
        • 场景二:大量的普通查询【指我们的业务中的大部分辅助性的查询】
          • 例如:
            • 我们的业务中的大部分辅助性的查询,如:取钱的时候先查询一下余额,根据用户的ID查询用户的记录,取得该用户最新的一条取钱记录等。我们肯定是要天天要用的,而且用的还非常多。
            • 我们的写入请求也是非常多的,导致大量的写入、查询操作压向同一数据库,然后,数据库挂了,系统挂了。所以要求我们必须分散数据库的压力,一个业界较成熟的方案就是数据库的读写分离,写的时候入主库,读的时候读从库。这样就把压力分散到不同的数据库了,如果一个读库性能不行,扛不住的话,可以一主多从,横向扩展。可谓是一剂良药啊!那怎么使用数据库的读写分离呢?一个一般的使用数据库的读写分离流程是这样的:
              • 服务端把一条业务数据落库
              • 数据库同步或异步或半同步把该条数据复制到从库
              • 服务端读数据的时候直接去从库读相应的数据
            • 读写分离中的问题:延迟问题【数据还没有到从库,我就马上读,那么是读不到的,会发生问题的。】
              在这里插入图片描述
              • 从库读不到就读主库
    • 微服务模式:
      • 对于这个模式来说,最难把握的是度,切记不要切分过细,一个功能一个子系统,上百个方法分成上百个子系统的就有点太过度了。实践中,一个较为可行的方法是:能不分就不分,除非有非常必要的理由!。
        • 优点:相对高性能,可扩展性强,高可用,适合于中等以上规模公司架构。
        • 缺点:复杂、度不好把握。指不仅需要一个能在高层把控大方向、大流程、总体技术的人,还需要能够针对各个子系统有针对性的开发。把握不好度或者滥用的话,这个模式适得其反!
          在这里插入图片描述
    • 多级缓存模式:一般在三个地方加入缓存,一个是客户端处,一个是API网关处,一个是具体的后端业务处,【但不是绝对的:实践中,要结合具体的实际情况,综合利用各级缓存技术,使得各种请求最大程度的在到达后端业务之前就被解决掉从而减少后端服务压力、减少占用带宽、增强用户体验。】
      在这里插入图片描述
      在这里插入图片描述
      • 客户端处缓存:这个地方加缓存可以说是效果最好的---无延迟。因为不用经过长长的网络链条去后端业务处获取数据,从而导致加载时间过长,客户流失等损失。虽然有CDN的支持,但是 从客户端到CDN还是有网络延迟的,虽然不大。
        • 具体的技术依据不同的客户端而定,对于WEB来讲,有浏览器本地缓存、Cookie、Storage、缓存策略等技术;
        • 对于APP来讲,有本地数据库、本地文件、本地内存、进程内缓存支持。如果客户端缓存没有命中,那么就会去后端业务拿数据,一般来讲,都会有个API网关,在这里加缓存也是非常有必要的
      • API网关处缓存:这个地方加缓存的好处是不用把请求发送到后方,直接在这里就处理了,然后返回给请求者。常见的技术,如http请求,API网关用的基本都是nginx,可以使用nginx本身的缓存模块,也可以使用Lua+Redis技术定制化
      • 后端业务处:Redis,Memcache,Jvm内等等。
    • 分库分表模式:
      在这里插入图片描述
      在这里插入图片描述
      • 比如用户表(user),单表数据量1亿,查询、插入、存储都出现了问题,怎么办呢?
        • 首先,分析问题,这个明显是由于数据量太大了而导致的问题
        • 其次,设计方案,
          • 可以分为10个库,这样每个库的数据量就降到了1KW
          • 单表1KW数据量还是有些大,而且不利于以后量的增长,所以每个库再分100个表,这个每个单表数据量就为10W了,对于查询、索引更新、单表文件大小、打开速度,都有一些益处。
        • 最后,逻辑实现。首先是写入数据,需要知道写到哪个分库分表中,读也是一样的,所以,需要有个请求路由层,负责把请求分发、转换到不同的库表中,一般有路由规则的概念
      • 这个分库分表模式的问题:
        • 主要是带来了事务上的问题,因为**分库分表,事务完成不了,而分布式事务又太笨重**,所以这里需要有一定的策略,保证在这种情况下事务能够完成。
          • 采取的策略如:最终一致性、复制、特殊设计等。
        • 再有就是业务代码的改造,一些关联查询要改造,一些单表orderBy的问题需要特殊处理,也包括groupBy语句
    • 弹性伸缩模式:
      在这里插入图片描述
      在这里插入图片描述
    • 多机房模式:
      在这里插入图片描述
      在这里插入图片描述

巨人的肩膀:
设计模式之禅
Head First 设计模式
K8S中文社区

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值