簡單理解,設計模式是前人多年寫程式碼踩坑總結出來的優秀程式碼攻略,目的是減少大量無用程式碼,讓專案更好維護
接下來要講的23種設計模式,但遵循下面的七大原則:
- 單一職責原則
2、介面隔離原則- 依賴倒置原則
4、里氏替換原則- 開閉原則
- 迪米特原則
- 合成複用原則
單一職責原則,目的是每個類履行一種職責。好比一個人是醫生,就好好幹醫生份內事,無需插手護士、保潔等和醫生職責無關事
比如說一個Person類,有工作work的方法:
public class Person{
public void work(String person){
System.out.println("職位為:"+ person + "的人正在工作");
}
}
這個Person,根據傳的person能做各種職位的事,破壞了單一職責
public interface Person{
public void work();
}
public class Doctor implement Person{
public void work(){
}
}
public class Teacher implement Person{
public void work(){
}
}
這個原則有點難講,總結就是把介面拆成最簡。
有一個Person,有 fight打人、eat吃飯、work工作三個方法
public interface Person{
public void fight();
public void eat();
public void work();
}
現在有兩個類:PersonA 和 PersonB 都實現了Person介面,實際上PersonA根本不需要打人,PersonB不需要工作。
public interface Person1{
public void eat();
}
public interface Person2 extend Person1{
public void fight();
}
public interface Person3 extend Person1{
public void work();
}
拆成3個介面。如此一來,PersonA只需要實現Person3,PersonAB實現Person2
依賴倒置原則,核心在於面向介面程式設計,傳細節,定抽象。
有個Person類,有send方法。
public class Person{
public void send(Email mail){
System.out.println(mail.getInfo());
}
}
這方法只能發郵件,那我傳簡訊啥的就不行了
定義3個介面
public interface Receiver{
public void getInfo();
}
public class Email implement Receiver{
public void getInfo(){
}
}
public class WeiXin implement Receiver{
public void getInfo(){
}
}
結果:
public class Person{
public void send(Receiver r){
System.out.println(mail.getInfo());
}
}
里氏替換原則,他的核心在於:儘量子類不用去重寫父類別的方法。
開閉原則,核心在於:對外允許拓展,對內拒絕修改。
什麼意思呢?比如現在我給你了空的羽毛球圓筒,對外只需要裝合適的羽毛球即可,對內是不拒絕像把羽毛球筒硬搞成方的
再舉個例子,一些組態檔有些引數是固定好的,比如 server.port。對內可以編寫自己的引數,對內是不允許修改server.port的引數名
迪米特原則,被依賴的類儘量讓依賴類知道的內部實現細節儘量少,總結就是降低耦合
合成複用原則,當使用另一個類的方法時,儘量使用聚合的方式,減少使用繼承。
這個比較好理解,現在有A類有個A1方法,B想要使用A1。不好的做法就是B去繼承A,然後就有A1方法。推薦是B裡引入A,然後呼叫A去用A1
設計模式可分為3種型別,下面列舉需要重點掌握的設計模式
建立型模式:單例模式,工廠模式,原型模式,建造者模式
結構型模式:介面卡模式,裝飾模式,代理模式
行為型模式:觀察者模式
單例模式,要求在整個應用中一個類只有一個物件範例。
剛開始此模式,懶漢和餓漢式的單例程式碼比較經典,大家有空去看下。
然後在spring最常見的單例就是我們的元件,預設都是單例的,也是單例的體現。
比如車有很多品牌:奧迪、寶馬、保時捷...如果我要一輛寶馬,那我得手動new,要一輛保時捷還是得new。那簡單工廠模式,在這裡可以定義一個建立車工廠類,傳入引數拿到不同品牌車。如:
Car car = CarFactory.getCar(String brand);
抽象工廠模式是簡單工廠模式的升級版。簡單工廠模式從一個工廠拿不同牌子的車,那如果某個牌子中細分有車型,顏色....那單個工廠顯然不夠用了
利用抽象工廠模式,可以從工廠方法A中拿到某個牌子的工廠B,再根據B拿到具體的車,如下:
BrandCarFactory brandCarFactory = CarFactory.getBrandCarFactory(String brand);
Car car = brandCarFactory.getCar(String color,String size....)
原型模式,通過原型物件構建出和他屬性一樣的範例。比如說建立10只羊,每次羊都叫喜羊羊,18歲。當第一隻建立後,第二隻的建立屬性都從第一支獲取
spring的bean設定為多例時,利用到的就是原型模式。
建造者模式,和工廠模式有點類似。說起來有點難講,還是看下例子:
public class Demo{
public static void main(String[] args) {
HouseDiretor dir = new HouseDiretor(new HightHouse());
House house = dir.buildHouse();
}
}
//房子構造者
public abstract class HouseBuilder{
private House house = new House();
public void buildA();
public void buildB();
public void buildC();
public void buildHouse(){
return house;
}
}
//高樓
public class HightHouse extends HouseBuilder{
public void buildA(){
...
}
public void buildB(){
...
}
public void buildC(){
....
}
}
//指揮者
public class HouseDiretor{
private HouseBuilder builder;
public HouseDiretor(HouseBuilder builder){
this.builder = builder;
}
public void buildHouse(){
builder.buildA();
builder.buildB();
builder.buildC();
return builder.buildHouse();
}
}
介面卡模式,就很好理解了。
平時大家見過的轉接頭就是介面卡,比如你筆電只有USB介面,但插口是HIDM,這就需要一個HIDM轉USB的轉換器充當連線。
下面以充電器將220V轉5V電壓作為介面卡來編寫例子:
5V介面:
public interface Inter5V{
public int output5V();
}
220V實現類
public class Impl220V {
public int output220V(){
return 220;
}
}
介面卡類
public class Impl5V extends Impl220V implement Inter5V{
public int output5V(){
return output220V()/24
}
}
比如現在手機要充電
public class Phone{
public void charging(Inter5V inter5V){
if(inter5V.output5V == 5){
//可以充電了
}else{
//不能充電
}
}
}
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new Impl55V)
}
類介面卡模式在於繼承,這種方式有侷限性。
而物件介面卡模式在於聚合,沒有繼承關係限制。還是按上面的例子,但是某些類發生變化:
介面卡類
public class Impl5V implement Inter5V{
private Impl220V impl220V;;
public Impl20V(Impl220V impl220V){
this.impl220V = impl220V;
}
public int output5V(){
return impl220V.output220V()/24
}
}
充電入口:
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new Impl55V(new Impl220V()))
}
介面介面卡模式,和上面兩種不太一樣。他解決是這樣一個問題:介面的方法不想全部實現,只實現自己要用的即可
介面介面卡往往會定義抽一個抽象類,去實現A介面的所有方法,但是方法體全為空。然後我們再去繼承抽象類,選擇性重寫自己的方法即可。
裝飾者模式,在於解決銀排列組合問題。就比如說牛肉麵,排骨麵...這些都是固定死的,理想狀態是一碗麵,想加啥自己加。
在Java在裝飾罩的體現,有個很重要的前提,就是被裝飾著和裝飾者都會實現或繼承相同的父類別,具體參考如下:
https://blog.csdn.net/m0_47944994/article/details/127901010
裝飾者A a = new 裝飾者A(new 被裝飾者)
裝飾者B b = new 裝飾者B(new 被裝飾者)
代理模式,在不改動A方法的基礎上,代理類(增強類)對A方法進行前後的增強。
代理模式可以分為三種:靜態代理,動態代理(JDK代理,介面代理),cglib代理
靜態代理,有個重要的前提是:代理類和被代理類都要實現同一介面,直接上例子:
同一介面:
public interface ITeateacher{
public void teach();
}
被代理類:
public class Teacher implments ITeateacher{
public void teach(){
System.out.println("教學生");
}
}
代理類:
public class TeacherProxy implments ITeateacher{
public ITeateacher teacher;
public TeacherProxy(ITeateacher teacher){
this.teacher = teacher;
}
public void teach(){
System.out.println("備課");
teacher.teacher();
System.out.println("下課");
}
}
使用:
public static void main(String[] args) {
ITeateacher teacher = new TeacherProxy(nwe Teacher());
teacher.teach()
}
好處就不說了,就是方法增強了。但缺點是實現同一介面,後面介面方法進行拓展不好維護
動態代理也被叫JDK代理,因為代理物件是依賴JDK的API來生成的,不需要我們去建立代理類。
前提要求是目標類是要實現介面的,並且是對介面的所有方法進行增強,範例如下:
目標類介面:
public interface ITeacherDao {
public String teach(String person);
}
目標類:
public class TeacherDao implements ITeacherDao{
@Override
public String teach(String person) {
return person + "在教書";
}
}
代理工廠類,我們編寫用於獲取代理物件:
public class ProxyFactory {
public ProxyFactory(Object targetObj){
this.targetObj = targetObj;
}
public Object getProxyIntance(){
return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces()
, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增強前程式碼");
Object returnValue = method.invoke(targetObj, args);
System.out.println(returnValue);
System.out.println("增強後程式碼");
return returnValue;
}
});
}
}
使用:
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new TeacherDao());
//獲取代理物件(JDK幫我們生成的)
ITeacherDao proxyIntance = (ITeacherDao) proxyFactory.getProxyIntance();
proxyIntance.teach("張三");
}
動態代理生成代理物件其實也實現了介面,和靜態一樣。不一樣是代理物件是JDK幫我們做了而已
cglib代理,如果說目標類是不需要實現任何介面的,那麼就用不了動態代理,但是cglib代理可以解決
注意cglib是一個框架包,需要諮詢引入使用。下面直接上程式碼:
目標類:
public class TeacherDao{
public String teach(String person) {
return person + "在教書";
}
}
代理工廠類:
public class ProxyFactory implements MethodInterceptor {
private Object targetObj;
public ProxyFactory(Object targetObj){
this.targetObj = targetObj;
}
public Object getProxyIntance(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetObj.getClass());//設定父類別,cglib原理就是通過繼承目標類來生成子類(代理類)
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib增強,增強前----");
Object returnValue = method.invoke(targetObj, args);
System.out.println(returnValue);
System.out.println("cglib增強,增強後----");
return returnValue;
}
}
使用
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new TeacherDao());
//獲取代理物件(JDK幫我們生成的)
TeacherDao proxyIntance = (TeacherDao) proxyFactory.getProxyIntance();
proxyIntance.teach("張三");
}
模板模式很簡單,就是定義了一套流程所需的介面和並且子類實現。比如RedisTemplate,JdbcTemplate