精华内容
下载资源
问答
  • 主要介绍了Java接口幂等性设计原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • java远程调用接口原理和范例

    千次阅读 2018-05-21 10:17:27
    Java 远程处理 Java远程方法调用... 创建一个简单的Java分布式远程方法调用程序可以按以下几个步骤操作, 一、定义远程接口: 在 Java 中,远程对象是实现远程接口的类的实例, 远程接口声明每个要远程调用的方法。在...
    Java 远程处理 
       Java远程方法调用(RMI)提供了Java程序语言的远程通讯功能,这种特性使客户机上运行的程序可以调用远程服务器上的对象,使Java编程人员能够在网络环境中分布操作。
       创建一个简单的Java分布式远程方法调用程序可以按以下几个步骤操作,
      
       一、定义远程接口:
       在 Java 中,远程对象是实现远程接口的类的实例, 远程接口声明每个要远程调用的方法。在需要创建一个远程对象的时候,我们通过传递一个接口来隐藏基层的实施细节,客户通过接口句柄发送消息即可。
       远程接口具有如下特点:
       1) 远程接口必须为public属性。如果不这样,除非客户端与远程接口在同一个包内,否则 当试图装入实现该远程接口的远程对象时,调用会得到错误结果。
       2) 远程接口必须扩展接口java.rmi.Remote。
       3) 除与应用程序本身特定的例外之外,远程接口中的每个方法都必须在自己的throws从句中 声明java.rmi.RemoteException。(或 RemoteException 的父类)。
       4) 作为参数或返回值传递的一个远程对象(不管是直接,还是本地对象中嵌入)必须声明为远 程接口,而不应声明为实施类。

    下面是远程接口的定义

    [java] view plaincopy
    package test;
    import java.rmi.Remote;
    import java.rmi.RemoteException;
    import java.math.BigInteger;

    public interface Fib extends Remote {
    public int getFib(int n) throws RemoteException;
    // public BigInteger getFib(BigInteger n) throws RemoteException;
    }

    二、实现远程接口:
       远程对象实现类必须扩展远程对象java.rmi.UnicastRemoteObject类,并实现所定义的远程接口。远程对象的实现类中包含实现每个远程接口所指定的远程方法的代码。这个类也可以含有附加的方法,但客户只能使用远程接口中的方法。因为客户是指向接口的一个句柄,而不是它的哪个类。必须为远程对象定义构造函数,即使只准备定义一个默认构造函数,用它调用基础类构造函数。因为基础类构造函数可能会抛出 java.rmi.RemoteException,所以即使别无它用必须抛出java.rmi.RemoteException例外。
       以下是远程对象实现类的声明:

    [java] view plaincopy
    package test;
    import java.math.BigInteger;
    import java.rmi.*;
    import java.rmi.server.UnicastRemoteObject;

    public class FibImp extends UnicastRemoteObject implements Fib {
    public FibImp() throws RemoteException {
    super();
    }

    public int getFib(int n) throws RemoteException {
    return n+2;
    }

    }

    三、编写服务器类:
       包含 main 方法的类可以是实现类自身,也可以完全是另一个类。下面通过RmiSampleServer 来创建一个远程对象的实例,并通过java.rmi.registry.LocateRegistry类的createRegistry 方法从指定端口号启动注册服务程序,也可以通过执行 rmiregistry 命令启动注册服务程序,注册服务程序的缺省运行端口为 1099。必须将远程对象名字绑定到对远程对象的引用上: Naming.rebind("//localhost:8808/SAMPLE-SERVER" , Server);
       以下是服务器类的声明:

    [java] view plaincopy
    package test;
    import java.net.MalformedURLException;
    import java.rmi.Naming;
    import java.rmi.RemoteException;
    import java.rmi.registry.LocateRegistry;
    public class FibonacciServer {
    /**
    * @param args
    */
    public static void main(String[] args) {
    try {
    LocateRegistry.createRegistry(8804);
    FibImp f = new FibImp();

    // 注册到 registry 中
    Naming.rebind("//localhost:8804/SAMPLE-SERVER", f);
    System.out.println("fib server ready");

    } catch (RemoteException re) {
    System.out.println("Exception in FibonacciImpl.main: " + re);
    } catch (MalformedURLException e) {
    System.out.println("MalformedURLException " + e);
    }
    }
    }

    四、编写使用远程服务的客户机类:
       客户机类的主要功能有两个,一是通过Naming.lookup方法来构造注册服务程序 stub 程序实例,二是调用服务器远程对象上的远程方法。
       以下是客户端类的声明:

    [java] view plaincopy
    package testClient;

    import test.Fib;
    import java.math.BigInteger;
    import java.net.MalformedURLException;
    import java.rmi.Naming;
    import java.rmi.NotBoundException;
    import java.rmi.RemoteException;
    public class FibClient {
    /**
    * @param args
    */
    public static void main(String[] args) {
    String url = "//localhost:8804/SAMPLE-SERVER";
    try {

    Fib calc = (Fib) Naming.lookup(url);
    for (int i = 0; i < 10; ++i) {
    int f = calc.getFib(i);
    System.out.println(f);
    }
    } catch (MalformedURLException e) {
    e.printStackTrace();
    } catch (RemoteException e) {
    e.printStackTrace();
    } catch (NotBoundException e) {
    e.printStackTrace();
    }
    }
    }
    展开全文
  • HDFS原理、命令行接口和Java接口

    千次阅读 2016-04-27 09:52:32
    1 HDFS的设计目标 我们都知道,Hadoop是一种用来进行海量数据存储和计算的分布式系统基础架构,它具有高效、低成本、高可靠(容错)及高扩展(可伸缩)等优点。HDFS作为Hadoop的核心之一,

    1 HDFS的设计目标

    我们都知道,Hadoop是一种用来进行海量数据存储和计算的分布式系统基础架构,它具有高效、低成本、高可靠(容错)及高扩展(可伸缩)等优点。
    (1)HDFS作为Hadoop的核心之一,它适合分布式存储超大文件,适合一次写入多次读取的文件访问模式,并且HDFS尤其具有高容错性和高吞吐量等优点。
    (2)HDFS不适合用于存储大量 的小文件,因为namenode将文件系统的元数据存储在内存中,因此该文件系统所能存储的文件总数受限于namenode的内存容量;HDFS不支持并发写以及文件任意位置的修改,因为这些操作比较低效;HDFS不适合低时间延迟的数据访问,因为HDFS是为高数据吞吐量应用优化的,这可能会以高时间延迟为代价,用HBase可以满足低延迟的访问需求。

    2 HDFS中的数据块(block)

    HDFS上的文件被划分为块大小的多个分块作为独立的存储单元。Hadoop2以后HDFS中的块的默认大小为128M。HDFS中小于一个块大小的文件不会占据整个块的空间。HDFS中的块比磁盘中的块大,其目的是为了最小化寻址开销,如果块设置得足够大,从磁盘传输数据的时间可以明显大于定位这个块开始位置所需的时间,这有利于由多个块组成的文件的传输;但是如果设置太过大,导致mapreduce中的map任务数太少(因为map通常一次处理一个块中的数据),少于集群中的节点数量,作业的运行速度就会比较慢。
    块的好处:
    (1)块可以保证大文件的分布式存储,一个文件的不同部分可能分别存储在不同的节点上;
    (2)使用块抽象而非整个文件作为存储单元,大大简化了存储子系统的设计;
    (3)块非常适合用于数据备份进而提供数据容错能力和可用性。

    3 NameNode和DataNode

    NameNode和DataNode分别为HDFS的主从节点。NameNode是整个文件系统的管理节点,它在内存中维护着整个文件系统的文件目录树,文件/目录的元数据以及每个文件对应的数据块列表,它接收客户端的操作请求。它在磁盘上还对应着fsimage文件和edits文件。DataNode是文件系统的工作节点,它根据需要存储并检索数据块,他接受客户端或NameNode的调度,并定期向NameNode发送它们所存储的块的列表。SecondaryNameNode是实现HA(高可用)的一种解决方案,而在Hadoop2以后的集群中使用的是主(active)备(stand by)NameNode这一方案来实现HA。


    4 HDFS的命令行接口(hadoop fs或hdfs dfs命令)

    l-help[cmd]  //显示命令的帮助信息
    l-ls(r)<path>  //显示当前目录下所有文件
    l-du(s)<path>  //显示目录中所有文件大小
    l-count[-q]<path>  //显示目录中文件数量
    l-mv<src><dst>  //移动多个文件到目标目录
    l-cp <src><dst>  //复制多个文件到目标目录
    l-rm(r)  //删除文件()
    l-put<localsrc><dst>  //本地文件复制到hdfs
    l-copyFromLocal  //put
    l-moveFromLocal  //从本地文件移动到hdfs
    l-get[-ignoreCrc]<src><localdst>  //复制文件到本地,可以忽略crc校验
    l-getmerge <src><localdst>  //将源目录中的所有文件排序合并到一个文件中
    l-cat<src>  //在终端显示文件内容
    l-text<src>  //在终端显示文件内容
    l-copyToLocal [-ignoreCrc]<src><localdst>  //复制到本地
    l-moveToLocal <src><localdst>
    l-mkdir<path>  //创建文件夹
    l-touchz<path>  //创建一个空文件

    5 HDFS的Java接口

    package captain.hadoop.hdfs;
    
    import java.io.FileInputStream;
    import java.net.URI;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.FSDataInputStream;
    import org.apache.hadoop.fs.FSDataOutputStream;
    import org.apache.hadoop.fs.FileSystem;
    import org.apache.hadoop.fs.Path;
    import org.apache.hadoop.io.IOUtils;
    
    //利用HDFS的Java接口实现目录的创建、删除,文件的上传、下载。
    public class HDFSDemo {
    
    	public static final String HDFS_PATH = "hdfs://hadoop:9000";
    	public static void main(String[] args) throws Exception{
    		
    		//注意需要root权限,需将"root"作为其参数
    		FileSystem fs = FileSystem.get(new URI(HDFS_PATH), new Configuration(), "root");
    		
    		//创建文件夹
    		makeDirectory(fs);
    		
    		//上传文件
    		uploadData(fs);
    		//另一种上传文件的方式:直接调用FileSystem的实例方法
    		//fs.copyFromLocalFile(new Path("D:/Eclipse_Workspace/Hadoop/log.txt"), new Path("/log"));
    		
    		//下载文件
    		downloadData(fs);
    		
    		//删除文件(夹)
    		deleteFile(fs);
    	}
    	
    	//删除文件函数
    	private static void deleteFile(FileSystem fs) throws Exception {
    		fs.delete(new Path("/testHDFS"), true);
    	}
    	
    	//下载文件函数
    	private static void downloadData(FileSystem fs) throws Exception {
    		FSDataInputStream in = fs.open(new Path("/testHDFS/log"));
    		IOUtils.copyBytes(in, System.out, 1024, true);
    	}
    	
    	//创建目录函数
    	private static void makeDirectory(FileSystem fs) throws Exception {
    		fs.mkdirs(new Path("/testHDFS"));
    	}
    	
    	//上传文件函数
    	private static void uploadData(FileSystem fs) throws Exception {
    		FileInputStream in = new FileInputStream("D:/Eclipse_Workspace/Hadoop/log.txt");
    		FSDataOutputStream out = fs.create(new Path("/testHDFS/log"));
    		IOUtils.copyBytes(in, out, 4096, true);
    	}
    
    }


    展开全文
  • java接口的意义

    万次阅读 多人点赞 2019-05-23 16:51:55
    1. java当中继承一个接口,要重写他的方法的话,那为什么还要多此一举的去实现一个接口呢? 直接把方法写在类当中不就可以了? 就是说去掉类名后面的Implements 接口 ,可以不可以呢? 统一访问: 接口的最主要的...

    1. java当中继承一个接口,要重写他的方法的话,那为什么还要多此一举的去实现一个接口呢?

    直接把方法写在类当中不就可以了?
    就是说去掉类名后面的Implements 接口 ,可以不可以呢?

    统一访问:
    接口的最主要的作用是达到统一访问,就是在创建对象的时候用接口创建,【接口名】 【对象名】=new 【实现接口的类】,这样你像用哪个类的对象就可以new哪个对象了,不需要改原来的代码,就和你的USB接口一样,插什么读什么,就是这个原理。如果我用接口,one.method1(); 那样我new a();就是用a的方法,new b()就是用b的方法

    这个就叫统一访问,因为你实现这个接口的类的方法名相同,但是实现内容不同。

    多重继承:
    另外java用接口还有一个好处,就是java不支持多重继承,但是可以实现多个接口,这个在某种程度上可以看做进行多重继承的一种办法。

    首先这样写是肯定没有错误的,在我们初期学期的时候还没有体现到接口的价值。但是当我们在做项目时,运用接口以后会很方便。因为接口里面只有简单的方法的声明。或者一些公用的东西。大家都去实现它的话比每次重新定义要方便很多。
    对于接口的作用,在一些小的项目上,很难看出其发挥的优势。这就使一些经常的做小项目的开发人员,做时间久了就感觉不到它有什么好的,有时候写起来还麻烦,干脆不用了。其实,在一些大项目上,接口的作用是发挥地相当的明显的。

    比如:
    如果你开发业务逻辑代码,当你好不容易的实现了它全部的功能,突然用户需求要改,你在修改你代码的同时,调用你代码的其它人也会改,如果代码关联性强的话,会有很多人都要改动代码,这样一来二去,程序会变得相当的不稳定,而且可能还会出现更多的新Bug,所有人都可能会陷入混乱。

    但如果使用接口的话,在你使用它之前,就要想好它要实现的全部功能(接口实际上就是将功能的封装)。确定下这个接口后,如果用户需求变了,你只要重新写它的实现类,而其它人只会调用你的接口,他不管你是怎么实现的,它只需要接口提供的功能。这样,很可能只需要把你的代码修改就可以了,其他人什么都不用做。

    同时:
    这样做的话,使得开发人员能够分工明确,只要确定下来接口了,就可以同时进行开发,提高开发效率。另外,使用接口还有使用方便,可读性强,结构清晰等优点。

    定义一个接口,可以有多种实现。

    解耦,可扩展这是设计接口的主要原因之一

    2. 为什么接口可以多继承,而类不可以?

    如果有两个父类,两个父类里有一个相同的方法,那么作为子类应该怎么继承这个方法?父类1的还是父类2的?
    但是实现多个接口则没问题,因为不管哪个接口,调用的都是同一个实现,因为只有方法名!
    而且单继承的目的之一,就是降低复杂度,减少维护难度

    • 继承:描述事物的自然属性和行为的复用。
    • 接口:描述事物的社会属性和行为的复用。

    因为继承父类包括实现,继承接口只包括接口,就是这样。
    接口定义的是行为,比如走,很多对象都可以实现这个行为
    但类定义的是对象的属性和具体行为

    class 负责实现, interface负责接口;
    多继承最麻烦的问题就是冲突, 冲突主要体现在 实现的时序和传入参数, 传出参数。这几个方面对于实现来说,父类发生时序问题时,使得语言本身变得无比复杂,而多继承问题在实现本身是可以通过很多方式解决的。而对于接口来说,传入参数冲突是overload,则不是问题,,只有传出参数这个问题是接口多继承不允许的例如:

    public interface IA {
    void doSomething();
    }
    public interface IB {
    Integer doSomething();
    }
    public interface IAB extends IA, IB {
    
    @Override	
    public void doSomething();
    
    @Override	
    public Integer doSomething();
    }
    
    

    这种情况编译器会告诉你: IA, IB 接口冲突,是不允许的。

    展开全文
  • 这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。 简单通俗的来说:一个类只负责一项职责。 问题:比如一个类T负责两个...

    在这里插入图片描述

    单一职责

    一个类,只有一个引起它变化的原因。应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。

    简单通俗的来说:一个类只负责一项职责。

    问题:比如一个类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。

    解决方法:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。

    扩展:说到单一职责原则,其实很多人不知不觉的都在使用,即使没有学习过设计模式的人,或者没有听过单一职责原则这个概念的人也会自觉的遵守这个重要原则,因为这是一个常识,比如你去在原有的项目上开发一个新的业务功能的时候,你肯定是会从新建立一个类,来实现一个新的功能,肯定不会基于原有的A功能身上直接写B业务的功能,肯定一般都是会新写一个类来实现B功能。在软件编程中,谁也不希望因为修改了一个功能导致其他的功能发生故障。而避免出现这一问题的方法便是遵循单一职责原则。虽然单一职责原则如此简单,并且被认为是常识,但是即便是经验丰富的程序员写出的程序,也会有违背这一原则的代码存在。为什么会出现这种现象呢?因为有职责扩散。所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。

    比如:类T只负责一个职责P,这样设计是符合单一职责原则的。后来由于某种原因,也许是需求变更了,也许是程序的设计者境界提高了,需要将职责P细分为粒度更细的职责P1,P2,这时如果要使程序遵循单一职责原则,需要将类T也分解为两个类T1和T2,分别负责P1、P2两个职责。但是在程序已经写好的情况下,这样做简直太费时间了。所以,简单的修改类T,用它来负责两个职责是一个比较不错的选择,虽然这样做有悖于单一职责原则。(这样做的风险在于职责扩散的不确定性,因为我们不会想到这个职责P,在未来可能会扩散为P1,P2,P3,P4……Pn。所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进行重构。)

    举例,用一个类描述动物呼吸这个场景:

    class Animal{
        public void breathe(String animal){
            System.out.println(animal+"呼吸空气");
        }
    }
    public class Client{
        public static void main(String[] args){
            Animal animal = new Animal();
            animal.breathe("牛");
            animal.breathe("羊");
            animal.breathe("猪");
        }
    }
    

    运行结果:

    牛呼吸空气
    羊呼吸空气
    猪呼吸空气
    

    在程序拓展维护的时候发现问题了,并不是所有的动物都呼吸空气的,比如鱼就是呼吸水的。修改时如果遵循单一职责原则,需要将Animal类细分为陆生动物类Terrestrial,水生动物Aquatic,代码如下:

    class Terrestrial{
        public void breathe(String animal){
            System.out.println(animal+"呼吸空气");
        }
    }
    class Aquatic{
        public void breathe(String animal){
            System.out.println(animal+"呼吸水");
        }
    }
    
    public class Client{
        public static void main(String[] args){
            Terrestrial terrestrial = new Terrestrial();
            terrestrial.breathe("牛");
            terrestrial.breathe("羊");
            terrestrial.breathe("猪");
            
            Aquatic aquatic = new Aquatic();
            aquatic.breathe("鱼");
        }
    }
    

    运行结果:

    牛呼吸空气
    羊呼吸空气
    猪呼吸空气
    鱼呼吸水
    

    然后发现如果这样修改花销是很大的,不利于后期的拓展和维护,除了将原来的类分解之外,还需要修改客户端。而直接修改类Animal来达成目的虽然违背了单一职责原则,但花销却小的多,代码如下:

    class Animal{
        public void breathe(String animal){
            if("鱼".equals(animal)){
                System.out.println(animal+"呼吸水");
            }else{
                System.out.println(animal+"呼吸空气");
            }
        }
    }
    
    public class Client{
        public static void main(String[] args){
            Animal animal = new Animal();
            animal.breathe("牛");
            animal.breathe("羊");
            animal.breathe("猪");
            animal.breathe("鱼");
        }
    }
    

    可以看到,这种修改方式要简单的多。但是却存在着隐患:有一天需要将鱼分为呼吸淡水的鱼和呼吸海水的鱼,则又需要修改Animal类的breathe方法,而对原有代码的修改会对调用“猪”“牛”“羊”等相关功能带来风险,也许某一天你会发现程序运行的结果变为“牛呼吸水”了。这种修改方式直接在代码级别上违背了单一职责原则,虽然修改起来最简单,但隐患却是最大的。还有一种修改方式:

    class Animal{
        public void breathe(String animal){
            System.out.println(animal+"呼吸空气");
        }
    
        public void breathe2(String animal){
            System.out.println(animal+"呼吸水");
        }
    }
    
    public class Client{
        public static void main(String[] args){
            Animal animal = new Animal();
            animal.breathe("牛");
            animal.breathe("羊");
            animal.breathe("猪");
            animal.breathe2("鱼");
        }
    }
    

    可以看到,这种修改方式没有改动原来的方法,而是在类中新加了一个方法,这样虽然也违背了单一职责原则,但在方法级别上却是符合单一职责原则的,因为它并没有动原来方法的代码。这三种方式各有优缺点,那么在实际编程中,采用哪一中呢?其实这真的比较难说,需要根据实际情况来确定。我的原则是:只有逻辑足够简单,才可以在代码级别上违反单一职责原则;只有类中方法数量足够少,才可以在方法级别上违反单一职责原则;
    比如本文所举的这个例子,它太简单了,它只有一个方法,所以,无论是在代码级别上违反单一职责原则,还是在方法级别上违反,都不会造成太大的影响。实际应用中的类都要复杂的多,一旦发生职责扩散而需要修改类时,除非这个类本身非常简单,否则还是遵循单一职责原则的好。

    遵循单一职责原的优点有:

    • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
    • 提高类的可读性,提高系统的可维护性;
    • 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。

    需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。

    单一职责看似简单,实际上在实际运用过程中,会发现真的会出现很多职责扩展的现象,这个时候采用直接违反还会方法上遵循还是完全遵循单一职责原则还是取决于当前业务开发的人员的技能水平和这个需求的时间,如果技能水平不足,肯定会简单的if else 去解决,不会想什么原则,直接实现功能就好了,这也是为什么在很多小公司会发现代码都是业务堆起来的,当然也有好的小公司代码是写的好的,这个也是不可否认的。不过不管采用什么方式解决,心中至少要知道有几种解决方法。

    开闭原则

    开闭原则(Open Closed Principle)是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的、灵活的系统。
    定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
    含义:一个软件实体应该通过扩展来实现变化,而不是通过修改已有代码来实现变化。

    软件实体包括以下几个部分:

    • 项目或软件产品中按照一定的逻辑规则划分的模块
    • 抽象和类
    • 方法

    开闭原则是为软件实体的未来事物而制定的对现行开发设计进行约束的一个原则。

    注意:开闭原则对扩展开放,对修改关闭,并不意味着不做任何修改,低层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段了。

    变化的类型:逻辑变化、子模块变化、可见试图变化。

    一个项目的基本路径应该是这样的:项目开发、重构、测试、投产、运维,其中的重构可以对原有的设计和代码进行修改,运维尽量减少对原有代码修改,保持历史代码的纯洁性,提高系统的稳定性。

    • 开闭原则的重要性:

      • 开闭原则对测试的影响
        开闭原则可是保持原有的测试代码仍然能够正常运行,我们只需要对扩展的代码进行测试就可以了。

      • 开闭原则可以提高复用性
        在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑。只有这样代码才可以复用,粒度越小,被复用的可能性就越大。

      • 开闭原则可以提高可维护性

      • 面向对象开发的要求

    如何使用开闭原则:

    • 抽象约束
      第一,通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法;
      第二,参数类型、引用对象尽量使用接口或者抽象类,而不是实现类;
      第三,抽象层尽量保持稳定,一旦确定即不允许修改。

    • 元数据(metadata)控制模块行为
      元数据就是用来描述环境和数据的数据,通俗地说就是配置参数,参数可以从文件中获得,也可以从数据库中获得。
      Spring容器就是一个典型的元数据控制模块行为的例子,其中达到极致的就是控制反转(Inversion of Control)

    • 制定项目章程
      在一个团队中,建立项目章程是非常重要的,因为章程中指定了所有人员都必须遵守的约定,对项目来说,约定优于配置。

    • 封装变化
      对变化的封装包含两层含义:
      第一,将相同的变化封装到一个接口或者抽象类中;
      第二,将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。

    依赖倒置原则 (Dependence Inversion Principle)

    所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体。实现开闭原则的关键是抽象化,并且从抽象化导出具体化实现,如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要手段。

    定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
    通俗点说:要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

    问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

    解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

    依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

    依赖倒置原则的核心思想是面向接口编程,我们依旧用一个例子来说明面向接口编程比相对于面向实现编程好在什么地方。
    传递依赖关系有三种方式:

    • 使用的方法是接口传递
    • 构造方法传递
    • setter方法传递

    在实际编程中,我们一般需要做到如下三点:

    • 低层模块尽量都要有抽象类或接口,或者两者都有。
    • 变量的声明类型尽量是抽象类或接口。
    • 使用继承时遵循里氏替换原则。

    依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。

    接口隔离原则 (Interface Segregation Principle)

    其原则字面的意思是:使用多个隔离的接口,比使用单个接口要好。本意降低类之间的耦合度,而设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

    原定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。

    问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。

    解决方案:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
    如图所示:
    在这里插入图片描述
    上图就没有实现接口隔离,类B 和 类 D 中都会实现不是自己的方法。
    如果接口过于臃肿,只要接口中出现的方法,不管对依赖于它的类有没有用处,实现类中都必须去实现这些方法,这显然不是好的设计。如果将这个设计修改为符合接口隔离原则,就必须对接口I进行拆分。在这里我们将原有的接口I拆分为三个接口,拆分后的设计如下图所示:
    在这里插入图片描述
    接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。本文例子中,将一个庞大的接口变更为3个专用的接口所采用的就是接口隔离原则。在程序设计中,依赖几个专用的接口要比依赖一个综合的接口更灵活。接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。

    说到这里,很多人会觉的接口隔离原则跟之前的单一职责原则很相似,其实不然。
    其一,单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离。
    其二,单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建。

    采用接口隔离原则对接口进行约束时,要注意以下几点:

    • 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
    • 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
    • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

    运用接口隔离原则,一定要适度,接口设计的过大或过小都不好。设计接口的时候,只有多花些时间去思考和筹划,才能准确地实践这一原则。

    合成/聚合复用原则(Composite/Aggregate Reuse Principle,CARP)

    定义:在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用这些对象的目的。应首先使用合成/聚合,合成/聚合则使系统灵活,其次才考虑继承,达到复用的目的。而使用继承时,要严格遵循里氏代换原则。有效地使用继承会有助于对问题的理解,降低复杂度,而滥用继承会增加系统构建、维护时的难度及系统的复杂度。

    如果两个类是“Has-a”关系应使用合成、聚合,如果是“Is-a”关系可使用继承。“Is-A"是严格的分类学意义上定义,意思是一个类是另一个类的"一种”。而"Has-A"则不同,它表示某一个角色具有某一项责任。

    什么是合成?什么是聚合?
    合成(Composition)和聚合(Aggregation)都是关联(Association)的特殊种类。
    聚合表示整体和部分的关系,表示“拥有”。如奔驰S360汽车,对奔驰S360引擎、奔驰S360轮胎的关系是聚合关系,离开了奔驰S360汽车,引擎、轮胎就失去了存在的意义。在设计中, 聚合不应该频繁出现,这样会增大设计的耦合度。
    合成则是一种更强的“拥有”,部分和整体的生命周期一样。合成的新的对象完全支配其组成部分,包括它们的创建和湮灭等。一个合成关系的成分对象是不能与另一个合成关系共享的。
    换句话说,合成是值的聚合(Aggregation by Value),而一般说的聚合是引用的聚合(Aggregation by Reference)。
    明白了合成和聚合关系,再来理解合成/聚合原则应该就清楚了,要避免在系统设计中出现,一个类的继承层次超过3层,则需考虑重构代码,或者重新设计结构。当然最好的办法就是考虑使用合成/聚合原则。

    • 通过合成/聚合来进行复用的优缺点

      • 优点:
      1. 新对象存取成分对象的唯一方法是通过成分对象的接口。
      2. 这种复用是黑箱复用,因为成分对象的内部细节是新对象所看不见的。
      3. 这种复用支持包装。
      4. 这种复用所需的依赖较少。
      5. 每一个新的类可以将焦点集中在一个任务上。
      6. 这种复用可以在运行时间内动态进行,新对象可以动态的引用与成分对象类型相同的对象。
      7. 作为复用手段可以应用到几乎任何环境中去。
      • 缺点:就是系统中会有较多的对象需要管理。
    • 通过继承来进行复用的优缺点

      • 优点:
        1.新的实现较为容易,因为超类的大部分功能可以通过继承的关系自动进入子类。
        2.修改和扩展继承而来的实现较为容易。
    • 缺点:

    1. 继承复用破坏包装,因为继承将超类的实现细节暴露给子类。由于超类的内部细节常常是对于子类透明的,所以这种复用是透明的复用,又称“白箱”复用。
    2. 如果超类发生改变,那么子类的实现也不得不发生改变。
    3. 从超类继承而来的实现是静态的,不可能在运行时间内发生改变,没有足够的灵活性。
    4. 继承只能在有限的环境中使用。

    迪米特法则(最少知道原则) (Demeter Principle)

    为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。也就是说一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。

    定义:一个对象应该对其他对象保持最少的了解。

    问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

    解决方案:尽量降低类与类之间的耦合。

    自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。

    迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。
    迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
    迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系,例如本例中,总公司就是通过分公司这个“中介”来与分公司的员工发生联系的。过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

    总结

    用抽象构建框架,用实现扩展细节的注意事项而已:

    • 单一职责原则告诉我们实现类要职责单一;
    • 里氏替换原则告诉我们不要破坏继承体系;
    • 依赖倒置原则告诉我们要面向接口编程;
    • 接口隔离原则告诉我们在设计接口的时候要精简单一;
    • 迪米特法则告诉我们要降低耦合。
    • 合成/聚合复用原则告诉我们开发程序要尽可能的简单高效;
    • 开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
    展开全文
  • Java中Iterator迭代器设计原理

    千次阅读 热门讨论 2016-02-14 18:15:12
    那么Iterator迭代器的设计原理是什么呢?迭代器问什么定义了一个借口,而不是一个类呢?  我们假设迭代器迭代数据的功能定义为了一个类,那么,会有这样的问题。不同的集合,由于数据结构不一样,所以他们的存储...
  • Java类加载原理解析

    千次阅读 2012-07-18 13:48:59
    Java类加载原理解析 1 基本信息 摘要: 每个java开发人员对java.lang.ClassNotFoundExcetpion这个异常肯定都不陌生,这背后就涉及到了java技术体系中的类加载。Java的类加载机制是java技术体系中比较核心的部分,...
  • Java并发API案例分析之并发设计原理

    千次阅读 多人点赞 2021-01-15 12:07:58
    并发、并行、同步、锁、线程安全……这一篇我记录了学习并发设计原理的关键知识,还有一些常用重要的Java并发编程API,学习一些关于并发程序设计的原理,弄懂来龙去脉,相对更加深入地理解并这部分知识。在学习理论...
  • Java常见设计模式总结

    万次阅读 多人点赞 2021-09-18 17:18:54
    项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。...
  • java编译原理

    千次阅读 2018-03-27 16:47:31
    4.Java编译原理1.javac是什么?(1)javac是一种编译器,能够将一种语言规范转换成另一种用语言规范,通常编译器是将便于人们理解的语言规范成机器容易理解的语言规范。(2)javac的任务就是将java源代码语言转换成jvm...
  • Java编译原理

    千次阅读 2015-09-03 13:36:27
    Java编译原理 1. 关于动态加载机制 学习Java比C++更容易理解OOP的思想,毕竟C++还混合了不少面向过程的成分。很多人都能背出来Java语言的特点,所谓的动态加载机制等等。当然概念往往是先记住而后消化的,可有...
  • java动态代理原理及解析

    万次阅读 多人点赞 2016-09-06 17:25:05
    java动态代理, jdk反射与代理模式
  • Java的Comparable接口&比较器原理

    千次阅读 2017-06-23 21:29:37
    需求:设计一个学生类, 属性有姓名,年龄, 成绩,并产生一个数组,要去安装成绩从高到低,如果成绩相等则有年龄有第到高排序。import java.util.Arrays; class Student implements Comparable<Student>{ private ...
  • 因此我们在接口设计时必须考虑防篡改校验,加签、验签就是用来解决这个问题的。划重点,敲黑板:加签、验签是用来解决防篡改问题的。 签名主要包含摘要和非对称加密两部分内容,首先对需要签名的数据做摘要(类似于...
  • Java MINA工作原理

    千次阅读 2015-04-13 16:27:14
    转自 :Mina工作原理分析 Mina是Apache社区维护的一个开源的高性能IO框架,在业界内久经考验,广为使用。Mina与后来兴起的高性能IO新贵Netty一样,都是韩国人Trustin Lee的大作,二者的设计理念是极为相似的。...
  • Java技术原理详解

    千次阅读 2015-10-20 22:15:47
    一、Java的起源 最早大概可追溯至1991年四月份,Sun MicroSystems公司的一个工程师Patrick Naughton 二、Java 运行原理 1、高级语言运行过程 2、Java语言的执行过程 三、 JVM的体系结构 四、JVM垃圾回收 五、JRE...
  • Java线程池实现原理深度分析

    万次阅读 2020-06-05 09:05:45
    2.1 总体设计 Java中的线程池核心实现类是ThreadPoolExecutor,本章基于JDK 1.8的源码来分析Java线程池的核心设计与实现。我们首先来看一下ThreadPoolExecutor的UML类图,了解下ThreadPoolExecutor的继承关系。 ...
  • java动态代理原理

    千次阅读 2018-10-24 17:48:52
     动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。  动态代理技术就是用来产生一个对象的代理对象的。在开发中为什么需要为...
  • 主要介绍了Java面向对象程序设计:抽象类,接口用法,结合实例形式分析了java抽象类与接口相关概念、原理、用法与操作注意事项,需要的朋友可以参考下
  • Java 接口回调机制

    千次阅读 2017-05-26 21:05:32
    在应用开发中,接口回调机制是一种常用的设计手段,也可以说是一种处理问题的模型,类之间,模块之间,都有一定的调用关系,一般来说,可以把使用某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口...
  • Java接口幂等性多种解决方案

    千次阅读 2020-11-25 11:34:36
    Java接口幂等性的解决方案: java语音中,同一个接口相同的参数多次和一次请求产生的效果是一样,这样的过程即被称为满足幂等性 //这中情况无论执行多少次,结果都不受影响,是幂等的。 update user set age = ...
  • 揭秘Java虚拟机:JVM设计原理与实现

    千次阅读 2018-12-25 08:57:33
    网站 ...gt;&...Java是一门非常流行的程序语言,但是,Java程序到底是如何运行的?如何写出更高效的Java代码……?这些令人困扰的问题,都可以从本书中找到答案。 &nbsp; 随着互联网的极速发...
  • Java 接口和抽象类

    千次阅读 2016-05-15 00:36:20
    介绍Java中的接口和抽象类 Java 接口和抽象类 版本号: 2018/9/29-1(22:40) 文章目录 Java 接口和抽象类 接口(27) Marker Interface functional interface default method 抽象类(18) 接口与抽象类区别(10)...
  • Java JDK 动态代理(AOP)使用及实现原理分析

    万次阅读 多人点赞 2019-05-08 21:28:06
    二、Java 动态代理类 三、JDK的动态代理怎么使用? 四、动态代理怎么实现的? 五、结论 一、什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为...
  • Java并发——线程池原理

    千次阅读 2015-01-24 19:56:15
    “池”技术对我们来说是非常熟悉的一个概念,它的引入是为了在某些场景下提高系统某些关键节点性能,最典型的例子就是数据库连接池,JDBC是一种服务供应接口(SPI),具体的数据库连接实现类由不同厂商实现,数据库...
  • JAVA接口的作用

    万次阅读 热门讨论 2007-05-31 21:36:00
    今天和同事好好的讨论了下接口原理和作用,发现原来自己的对接口的理解仅仅是局限在概念的高度抽象上,觉得好像理解了但是不会变化应用其实和没有理解差不多。以前看一个帖子说学习一个东西不管什么时候都要带着...
  • Java编译原理(有感)

    千次阅读 2012-06-11 10:30:04
    Java编译原理   1. 关于动态加载机制 学习Java比C++更容易理解OOP的思想,毕竟C++还混合了不少面向过程的成分。很多人都能背出来Java语言的特点,所谓的动态加载机制等等。当然概念往往是先记住而后消化的,可...
  • java动态代理原理解析

    千次阅读 2017-11-29 17:23:21
    总结:一、应用:1、要代理的类必须有对应实现接口。2、被增强的代码要实现invocationHandle接口,实现接口的invoke方法,在方法里添加增强代码和通过调用method.invoke( proxied, args); 实现对源目标方法的调用。...
  • java文件上传原理

    千次阅读 2005-05-20 16:59:00
    网页表单是一个web基础设施,提供了交互接口,作为使用web技术的开发人员,了解其工作原理,是必须的功课,本文描述表单域和二进制数据一同传输并被处理. 有关表单资料,若需要可参考...
  • 实验课程名称 Java 语言程序设计 A 实验项目名称 实验 3 接口 实验成绩 实 验 者 专业班级 组 别 同 组 者 无 开始日期 第一部分实验预习报告 包括实验目的及意义实验基本原理与方法主要仪器设 备及耗材实验内容及...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 227,017
精华内容 90,806
关键字:

java接口设计原理

java 订阅