精华内容
下载资源
问答
  • 对一位管理者的访问
    千次阅读
    2022-03-18 16:52:35

    您企业内的用户身份真的安全吗?

    归根结底,这一问题的答案取决于企业身份和访问管理(IAM)策略的严谨程度。IAM 的概念比较宽泛,指企业如何管理用户对设备、文件、网络和应用的访问权限,而 IAM 安全策略的着眼点更具体,主要指企业为降低身份相关的风险而制定的策略体系。

    随着远程办公不断普及,设置严谨的 IAM 安全策略既能为员工提供办公所需的访问权限,又能保护企业的资产安全,对企业来说至关重要。制定 IAM 安全策略时,首先要了解其含义、实施方法以及实施原因。

    什么是 IAM 安全策略?

    IAM 安全策略包含了用于保护企业用户身份、防止恶意攻击或无意泄露的所有流程、系统和应用。IAM 基本都和管理员有关,管理员需要保护用户安全、帮助用户流畅地访问企业 IT 资源,IAM 安全策略就是要用尽可能低风险的方式实现这一流程。

    IAM 安全策略就像是企业与来往用户之间的一道护城河。在用户已经经过适当的筛选、许可和预配的情况下,设置这道护城河只是为了有备无患。而在其他防护手段漏洞百出且网络入侵者试图袭击企业网络的情况下,IAM 安全策略将是最后一道防线。这道额外的安全层能保护企业核心资产避免落入不法分子手中。

    IAM 安全策略的优势    

    简单来说,实施 IAM 安全策略的确具有几大优势,但不实施这一策略产生的后果却是企业承担不起的。IBM 2021年发布的一份报告显示,数据泄露事件对企业造成的平均损失高达424万美元,其中最常见的诱因是凭证窃取。IT 部门作为企业用户身份的管理者要预防网络攻击就必须制定严格的 IAM 安全策略。

    1)快速升级防护  

    现代 IAM 安全解决方案为企业提供了安全防护的黄金准则,自动化的流程也让运维更便捷。

    企业可以通过单向的加盐哈希密码、多因素认证、密码复杂度设置和 SSH 密钥管理减少身份泄露,显著提升企业的安全状况。

    2)提高 IT 部门效率

    对管理员来说,今天的 IAM 安全方案比传统的本地方案更容易管理。企业在制定安全策略时,应考虑 IT 人员的可用性和工作量。许多现代 IAM 平台都提供自动化的远程管理系统,让 IT 部门能腾出时间完成更复杂的任务。

    IAM 安全策略范例

    虽然 IAM 安全策略还没有通用的实现方式,企业还是可以参考以下提示将安全性提升到最高,同时尽可能为 IT 部门减负。秘诀就是找到合适的远程软件,实施零信任安全策略,并要求用户在设备上启用多因素认证

    1)远程化

    远程化是 IAM 安全策略的必要部分,如果企业现有的 IAM 安全策略不是基于云的,那么远程化就是企业布局 IAM 安全策略的第一步。远程访问不仅对在家办公的员工连接企业网络时至关重要,而且能让 IT 部门随时随地进行故障排除和安全策略的管理。 

    当 IAM 安全系统驻留在本地时,远程终端用户必须断开 VPN 才能办公,这就给管理员的系统维护工作增加了负担,牺牲了其他任务时间。解决密码更改等大量支持请求时如果还需要 VPN 访问,情况就变得更复杂,这时 IT 支持人员只能采取其他方法,而终端用户只能焦急等待。

    IAM 安全流程的远程化帮助企业员工轻松访问内网的同时还为管理员持续提供无缝监控,一举两得。

    2)实施零信任策略

    随着现场办公逐渐向混合办公转变,用户可以从家庭 WiFi、公共咖啡馆等任何地方登录,对身份验证安全的需求由此产生。为此,企业可以采用零信任安全策略。

    零信任策略并不是一种自动身份验证,而是规定在用户或设备只有通过信任模型的身份验证后才能访问系统、应用或网络。仅仅因为用户持有正确的凭证并不代表该用户就是被授权使用该凭证的人。

    要在企业中实施零信任策略,需要抓住一切机会在访问业务中增加身份核验步骤。


    管理员仍应设置严格、复杂的密码要求,不过在此基础上使用条件访问策略或在身份验证过程中使用多因素认证对于防止恶意用户利用窃取凭据很有帮助。其中,条件访问策略指仅批准受信任网络或私有网络上的管理设备进行访问,而多因素认证将在下节详细展开。

    3)启用多因素认证MFA

    MFA 是创建 IAM 安全策略最简单也最有效的一种方法,它要求使用多个凭证进行验证,自动加强对用户身份的保护。MFA 中的凭证通常涉及静态密码或 PIN 码,以及一个或多个附加验证因素,如安全问题、发送到另一台设备的验证码、确认来自身份验证APP的推送通知,甚至提供指纹或视网膜等生物识别信号。

    由于静态密码任何人都能获取,MFA 的其他验证因素通常设计为用户的持有物、生物特征或行为特征。虽然密码很容易在数据泄露中被窃取,但攻击者即便知道了密码,也很难访问手机获取验证码或猜出随机的六位 TOTP 口令。

    宁盾双因素认证(2FA)就是一种最常见的 MFA 方式,只需要在密码以外再采用一个验证因素,通常为OTP(动态密码)。除此之外,宁盾双因素认证还研发创新了适合国内社交环境的企微、飞书、钉钉H5令牌,无需下载任何APP,通过员工手机端/电脑端的企微、飞书、钉钉工作台中的小程序令牌即可获知动态密码;还有企微、飞书、钉钉的扫码认证,更加便捷。

    由于 2FA 可以平衡企业的安全性和用户体验的便利性,很多公司更愿意使用 2FA,如果企业想进一步提升安全性,可以根据需要设置尽可能多的验证步骤。

    为您的企业选择合适的 IAM 安全解决方案

    在当今不断发展的混合办公场景下,如何平衡隐私和安全成为企业的一大难题。IT 部门需要在不影响 IAM 安全策略的情况下提高远程访问的效率,使其更易于管理。

    针对这一问题,宁盾的解决方案是一体化身份认证平台(IAM系统),同时将于今年推出基于云的 IAM 管理解决方案(IDaaS平台),让管理员可以统一对整体 IAM 安全策略进行无缝管理。

    更多相关内容
  • Java设计模式之访问者模式

    千次阅读 2018-05-08 15:34:32
    访问者模式是种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用...

    转载:https://blog.csdn.net/u012124438/article/details/70537203

    访问者模式,是行为型设计模式之一。访问者模式是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用它了。

    访问者模式的基本想法是,软件系统中拥有一个由许多对象构成的、比较稳定的对象结构,这些对象的类都拥有一个 accept 方法用来接受访问者对象的访问。访问者是一个接口,它拥有一个 visit 方法,这个方法对访问到的对象结构中不同类型的元素做出不同的处理。在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施 accept 方法,在每一个元素的 accept 方法中会调用访问者的 visit 方法,从而使访问者得以处理对象结构的每一个元素,我们可以针对对象结构设计不同的访问者类来完成不同的操作,达到区别对待的效果。

    定义及使用场景

    定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。

    可以对定义这么理解:有这么一个操作,它是作用于一些元素之上的,而这些元素属于某一个对象结构。同时这个操作是在不改变各元素类的前提下,在这个前提下定义新操作是访问者模式精髓中的精髓。

    使用场景: 
    (1)对象结构比较稳定,但经常需要在此对象结构上定义新的操作。

    (2)需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

    UML图

    这里写图片描述

    (1)Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。

    (2)ConcreteVisitor1、ConcreteVisitor2:具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。

    (3)Element:元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。

    (4)ConcreteElementA、ConcreteElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

    (5)ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。

    访问者模式的简单例子

    我们都知道财务都是有账本的,这个账本就可以作为一个对象结构,而它其中的元素有两种,收入和支出,这满足我们访问者模式的要求,即元素的个数是稳定的,因为账本中的元素只能是收入和支出。

    而查看账本的人可能有这样几种,比如老板,会计事务所的注会,财务主管,等等。而这些人在看账本的时候显然目的和行为是不同的。

    首先我们给出单子的接口,它只有一个方法accept。

    //单个单子的接口(相当于Element)
    public interface Bill {
    
        void accept(AccountBookViewer viewer);
    
    }

    其中的方法参数AccountBookViewer是一个账本访问者接口,接下来也就是实现类,收入单子和消费单子,或者说收入和支出类。

    //消费的单子
    public class ConsumeBill implements Bill{
    
        private double amount;
    
        private String item;
    
        public ConsumeBill(double amount, String item) {
            super();
            this.amount = amount;
            this.item = item;
        }
    
        public void accept(AccountBookViewer viewer) {
            viewer.view(this);
        }
    
        public double getAmount() {
            return amount;
        }
    
        public String getItem() {
            return item;
        }
    
    }
    //收入单子
    public class IncomeBill implements Bill{
    
        private double amount;
    
        private String item;
    
        public IncomeBill(double amount, String item) {
            super();
            this.amount = amount;
            this.item = item;
        }
    
        public void accept(AccountBookViewer viewer) {
            viewer.view(this);
        }
    
        public double getAmount() {
            return amount;
        }
    
        public String getItem() {
            return item;
        }
    
    }

    上面最关键的还是里面的accept方法,它直接让访问者访问自己,这相当于一次静态分派(文章最后进行解释),当然我们也可以不使用重载而直接给方法不同的名称。

    接下来是账本访问者接口

    //账单查看者接口(相当于Visitor)
    public interface AccountBookViewer {
    
        //查看消费的单子
        void view(ConsumeBill bill);
    
        //查看收入的单子
        void view(IncomeBill bill);
    
    }

    这两个方法是重载方法,就是在上面的元素类当中用到的,当然你也可以按照访问者模式类图当中的方式去做,将两个方法分别命名为viewConsumeBill和viewIncomeBill,而一般建议按照类图上来做的

    访问者的实现

    //老板类,查看账本的类之一
    public class Boss implements AccountBookViewer{
    
        private double totalIncome;
    
        private double totalConsume;
    
        //老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心
        public void view(ConsumeBill bill) {
            totalConsume += bill.getAmount();
        }
    
        public void view(IncomeBill bill) {
            totalIncome += bill.getAmount();
        }
    
        public double getTotalIncome() {
            System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
            return totalIncome;
        }
    
        public double getTotalConsume() {
            System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
            return totalConsume;
        }
    
    }
    //注册会计师类,查看账本的类之一
    public class CPA implements AccountBookViewer{
    
        //注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
        public void view(ConsumeBill bill) {
            if (bill.getItem().equals("工资")) {
                System.out.println("注会查看工资是否交个人所得税。");
            }
        }
        //如果是收入,则所有的收入都要交税
        public void view(IncomeBill bill) {
            System.out.println("注会查看收入交税了没。");
        }
    
    }

    老板只关心收入和支出的总额,而注会只关注该交税的是否交税

    接下来是账本类,它是当前访问者模式例子中的对象结构

    //账本类(相当于ObjectStruture)
    public class AccountBook {
        //单子列表
        private List<Bill> billList = new ArrayList<Bill>();
        //添加单子
        public void addBill(Bill bill){
            billList.add(bill);
        }
        //供账本的查看者查看账本
        public void show(AccountBookViewer viewer){
            for (Bill bill : billList) {
                bill.accept(viewer);
            }
        }
    }

    账本类当中有一个列表,这个列表是元素(Bill)的集合,这便是对象结构的通常表示,它一般会是一堆元素的集合,不过这个集合不一定是列表,也可能是树,链表等等任何数据结构,甚至是若干个数据结构。其中show方法,就是账本类的精髓,它会枚举每一个元素,让访问者访问。

    测试客户端

    public class Client {
    
        public static void main(String[] args) {
            AccountBook accountBook = new AccountBook();
            //添加两条收入
            accountBook.addBill(new IncomeBill(10000, "卖商品"));
            accountBook.addBill(new IncomeBill(12000, "卖广告位"));
            //添加两条支出
            accountBook.addBill(new ConsumeBill(1000, "工资"));
            accountBook.addBill(new ConsumeBill(2000, "材料费"));
    
            AccountBookViewer boss = new Boss();
            AccountBookViewer cpa = new CPA();
    
            //两个访问者分别访问账本
            accountBook.show(cpa);
            accountBook.show(boss);
    
            ((Boss) boss).getTotalConsume();
            ((Boss) boss).getTotalIncome();
        }
    }

    上面的代码中,可以这么理解,账本以及账本中的元素是非常稳定的,这些几乎不可能改变,而最容易改变的就是访问者这部分。

    访问者模式最大的优点就是增加访问者非常容易,我们从代码上来看,如果要增加一个访问者,你只需要做一件事即可,那就是写一个类,实现AccountBookViewer接口,然后就可以直接调用AccountBook的show方法去访问账本了。

    如果没使用访问者模式,一定会增加许多if else,而且每增加一个访问者,你都需要改你的if else,代码会显得非常臃肿,而且非常难以扩展和维护。

    静态分派以及动态分派

    变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type);而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)。比如:

    List list = null;
    list = new ArrayList();

    声明了一个变量list,它的静态类型(也叫明显类型)是List,而它的实际类型是ArrayList。根据对象的类型而对方法进行的选择,就是分派(Dispatch),分派(Dispatch)又分为两种,即静态分派和动态分派。静态分派(Static Dispatch)发生在编译时期,分派根据静态类型信息发生。

    静态分派 
    静态分派就是按照变量的静态类型进行分派,从而确定方法的执行版本,静态分派在编译时期就可以确定方法的版本。而静态分派最典型的应用就是方法重载

    public class Main {
    
        public void test(String string){
            System.out.println("string");
        }
    
        public void test(Integer integer){
            System.out.println("integer");
        }
    
        public static void main(String[] args) {
            String string = "1";
            Integer integer = 1;
            Main main = new Main();
            main.test(integer);
            main.test(string);
        }
    }

    在静态分派判断的时候,我们根据多个判断依据(即参数类型和个数)判断出了方法的版本,那么这个就是多分派的概念,因为我们有一个以上的考量标准,也可以称为宗量。所以JAVA是静态多分派的语言。

    动态分派 
    对于动态分派,与静态相反,它不是在编译期确定的方法版本,而是在运行时才能确定。而动态分派最典型的应用就是多态的特性

    interface Person{
        void test();
    }
    class Man implements Person{
        public void test(){
            System.out.println("男人");
        }
    }
    class Woman implements Person{
        public void test(){
            System.out.println("女人");
        }
    }
    public class Main {
    
        public static void main(String[] args) {
            Person man = new Man();
            Person woman = new Woman();
            man.test();
            woman.test();
        }
    }

    这段程序输出结果为依次打印男人和女人,然而这里的test方法版本,就无法根据man和woman的静态类型去判断了,他们的静态类型都是Person接口,根本无从判断。

    显然,产生的输出结果,就是因为test方法的版本是在运行时判断的,这就是动态分派。

    动态分派判断的方法是在运行时获取到man和woman的实际引用类型,再确定方法的版本,而由于此时判断的依据只是实际引用类型,只有一个判断依据,所以这就是单分派的概念,这时我们的考量标准只有一个宗量,即变量的实际引用类型。相应的,这说明JAVA是动态单分派的语言。

    访问者模式中的伪动态双分派

    访问者模式中使用的是伪动态双分派,所谓的动态双分派就是在运行时依据两个实际类型去判断一个方法的运行行为,而访问者模式实现的手段是进行了两次动态单分派来达到这个效果。

    回到上面例子当中账本类中的accept方法

    for (Bill bill : billList) {
                bill.accept(viewer);
            }

    这里就是依据biil和viewer两个实际类型决定了view方法的版本,从而决定了accept方法的动作。

    分析accept方法的调用过程 
    1.当调用accept方法时,根据bill的实际类型决定是调用ConsumeBill还是IncomeBill的accept方法。

    2.这时accept方法的版本已经确定,假如是ConsumeBill,它的accept方法是调用下面这行代码。

     public void accept(AccountBookViewer viewer) {
            viewer.view(this);
        }

    此时的this是ConsumeBill类型,所以对应于AccountBookViewer接口的view(ConsumeBill bill)方法,此时需要再根据viewer的实际类型确定view方法的版本,如此一来,就完成了动态双分派的过程。

    以上的过程就是通过两次动态双分派,第一次对accept方法进行动态分派,第二次对view(类图中的visit方法)方法进行动态分派,从而达到了根据两个实际类型确定一个方法的行为的效果。

    而原本我们的做法,通常是传入一个接口,直接使用该接口的方法,此为动态单分派,就像策略模式一样。在这里,show方法传入的viewer接口并不是直接调用自己的view方法,而是通过bill的实际类型先动态分派一次,然后在分派后确定的方法版本里再进行自己的动态分派。

    注意:这里确定view(ConsumeBill bill)方法是静态分派决定的,所以这个并不在此次动态双分派的范畴内,而且静态分派是在编译期就完成的,所以view(ConsumeBill bill)方法的静态分派与访问者模式的动态双分派并没有任何关系。动态双分派说到底还是动态分派,是在运行时发生的,它与静态分派有着本质上的区别,不可以说一次动态分派加一次静态分派就是动态双分派,而且访问者模式的双分派本身也是另有所指。

    这里的this的类型不是动态确定的,你写在哪个类当中,它的静态类型就是哪个类,这是在编译期就确定的,不确定的是它的实际类型,请各位区分开这一点。

    对访问者模式的一些思考

    假设我们上面的例子当中再添加一个财务主管,而财务主管不管你是支出还是收入,都要详细的查看你的单子的项目以及金额,简单点说就是财务主管类的两个view方法的代码是一样的。

    这里的将两个view方法抽取的方案是,我们可以将元素提炼出层次结构,针对层次结构提供操作的方法,这样就实现了优点当中最后两点提到的针对层次定义操作以及跨越层次定义操作。

    //单个单子的接口(相当于Element)
    public interface Bill {
    
        void accept(Viewer viewer);
    
    }
    //抽象单子类,一个高层次的单子抽象
    public abstract class AbstractBill implements Bill{
    
        protected double amount;
    
        protected String item;
    
        public AbstractBill(double amount, String item) {
            super();
            this.amount = amount;
            this.item = item;
        }
    
        public double getAmount() {
            return amount;
        }
    
        public String getItem() {
            return item;
        }
    
    }
    //收入单子
    public class IncomeBill extends AbstractBill{
    
        public IncomeBill(double amount, String item) {
            super(amount, item);
        }
    
        public void accept(Viewer viewer) {
            if (viewer instanceof AbstractViewer) {
                ((AbstractViewer)viewer).viewIncomeBill(this);
                return;
            }
            viewer.viewAbstractBill(this);
        }
    
    }
    //消费的单子
    public class ConsumeBill extends AbstractBill{
    
        public ConsumeBill(double amount, String item) {
            super(amount, item);
        }
    
        public void accept(Viewer viewer) {
            if (viewer instanceof AbstractViewer) {
                ((AbstractViewer)viewer).viewConsumeBill(this);
                return;
            }
            viewer.viewAbstractBill(this);
        }
    
    }

    这是元素类的层次结构,可以看到,我们的accept当中出现了if判断,这里的判断是在判断一个层次,这段代码是不会被更改的。

    访问者层次

    //超级访问者接口(它支持定义高层操作)
    public interface Viewer{
    
        void viewAbstractBill(AbstractBill bill);
    
    }
    //比Viewer接口低一个层次的访问者接口
    public abstract class AbstractViewer implements Viewer{
    
        //查看消费的单子
        abstract void viewConsumeBill(ConsumeBill bill);
    
        //查看收入的单子
        abstract void viewIncomeBill(IncomeBill bill);
    
        public final void viewAbstractBill(AbstractBill bill){}
    }
    //老板类,查看账本的类之一,作用于最低层次结构
    public class Boss extends AbstractViewer{
    
        private double totalIncome;
    
        private double totalConsume;
    
        //老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心
        public void viewConsumeBill(ConsumeBill bill) {
            totalConsume += bill.getAmount();
        }
    
        public void viewIncomeBill(IncomeBill bill) {
            totalIncome += bill.getAmount();
        }
    
        public double getTotalIncome() {
            System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
            return totalIncome;
        }
    
        public double getTotalConsume() {
            System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
            return totalConsume;
        }
    
    }
    //注册会计师类,查看账本的类之一,作用于最低层次结构
    public class CPA extends AbstractViewer{
    
        //注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
        public void viewConsumeBill(ConsumeBill bill) {
            if (bill.getItem().equals("工资")) {
                System.out.println("注会查看是否交个人所得税。");
            }
        }
        //如果是收入,则所有的收入都要交税
        public void viewIncomeBill(IncomeBill bill) {
            System.out.println("注会查看收入交税了没。");
        }
    
    }
    //财务主管类,查看账本的类之一,作用于高层的层次结构
    public class CFO implements Viewer {
    
        //财务主管对每一个单子都要核对项目和金额
        public void viewAbstractBill(AbstractBill bill) {
            System.out.println("财务主管查看账本时,每一个都核对项目和金额,金额是" + bill.getAmount() + ",项目是" + bill.getItem());
        }
    
    }

    财务主管(CFO)是针对AbstractBill这一层定义的操作,而原来的老板(Boss)和注册会计师(CPA)都是针对ConsumeBill和IncomeBill这一层定义的操作,这时已经产生了跨越层次结构的行为,老板和注册会计师都跨过了抽象单子这一层,直接针对具体的单子定义操作。

    账本类没有变化,最后看客户端的使用

    public class Client {
    
        public static void main(String[] args) {
            AccountBook accountBook = new AccountBook();
            //添加两条收入
            accountBook.addBill(new IncomeBill(10000, "卖商品"));
            accountBook.addBill(new IncomeBill(12000, "卖广告位"));
            //添加两条支出
            accountBook.addBill(new ConsumeBill(1000, "工资"));
            accountBook.addBill(new ConsumeBill(2000, "材料费"));
    
            Viewer boss = new Boss();
            Viewer cpa = new CPA();
            Viewer cfo = new CFO();
    
            //两个访问者分别访问账本
            accountBook.show(cpa);
            accountBook.show(boss);
            accountBook.show(cfo);
    
            ((Boss) boss).getTotalConsume();
            ((Boss) boss).getTotalIncome();
        }
    }

    回想一下,要是再出现和财务主管一样对所有单子都是一样操作的人,我们就不需要复制代码了,只需要让他实现Viewer接口就可以了,而如果要像老板和注会一样区分单子的具体类型,则继承AbstractViewer就可以。

    总结

    优点:

    1、使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。

    2、添加新的操作或者说访问者会非常容易。

    3、将对各个元素的一组操作集中在一个访问者类当中。

    4、使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。

    5、可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。

    缺点:

    1、增加新的元素会非常困难。

    2、实现起来比较复杂,会增加系统的复杂性。

    3、破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构,就像收入和支出类必须提供访问金额和单子的项目的方法一样。

    适用性:

    1、数据结构稳定,作用于数据结构的操作经常变化的时候。

    2、当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式。

    3、有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。

    展开全文
  • 这是我最喜欢的篇文章!如何成为优秀的管理者?其实很简单,做好两件事:(1)确保团队能够工作;(2)关心他们,把他们当人看。

    婚礼、旅行和管理者之间有什么共同之处吗?跟任何一个成年人谈论这些话题,你肯定会听到一个可怕的故事。在婚礼上,那是关于喝醉的客人、糟糕的天气或者不合时宜的失言。在旅行过程中,那是关于丢失的行李、混乱的乘客或者慌乱的转机。至于管理者,那是关于你以前的一位上司的故事,他可恨、不胜任、毫无头绪、傲慢自大、感觉迟钝、密谋策划、眼睛如珠、没有骨气、自私自利、性情古怪……倒并不是我对以前的上司怀恨在心。

    抛开极端的情况,大部分关于婚礼和旅行的可怕故事都可以面带微笑地被重述出来,还能给大家带来笑声。但关于管理者的可怕故事却不行。当然,所有人都能对以前上司所做之事的愚蠢或荒谬大笑一场,但当你观察那个曾经遭罪的员工的眼睛时,你会发现那里总是装满了藐视。那个员工还没有原谅或者忘记以前的那位管理者的所作所为。

    持续送出的赠品

    为什么人们这么容易忘记婚礼上或旅行过程中的倒霉事,但对管理者的不正当行为却始终耿耿于怀呢?那是因为在婚礼结束的时候,新婚夫妇会甜蜜地接吻。在漫长旅途的终点,你最后回到了家。但糟糕的管理者还在——没有开心的结局。糟糕的管理者呆在那里,每天都在,日复一日,干着一件又一件的坏事。 

    即使你最后逃离了某位糟糕管理者的魔掌,他对你已经造成的影响却无法挽回:消逝的时间、错失的机会和丢失的成果。他的评语和行为时常萦绕你心头,阴魂不散。他过去的冷酷伤害了你当前的认知,还有你对价值的真实感觉。你开始对所有管理者有阴影,不再信任他们。过去一年内遭受的创伤,却需要10年的功夫去修复——那还只是由一个管理者引起的。

    对公司造成的损害就更大了。堕落的管理者造成生产力的丧失、浪费和返工、糟糕的质量、浮躁的行为,还有心境不佳的员工,他们要么满腹牢骚地离开公司,要么继续留在公司消极怠工。堕落的管理者也有可能招来法律诉讼,不过那跟本栏目的主题偏离得太远了。

    优秀就够了 

    “嘿,让管理者得以片刻的喘息吧,”有些人可能哭诉道,“成为一名优秀的管理者很难啊!”不对,那不难。成为一名卓越的管理者才难。而成为一名优秀的管理者很容易。优秀的管理者只需要关注两件事情——两件非常简单的事情,任何人都能做到:

    1. 确保他的员工能够工作。
    2. 关心他的员工。

    就这么简单!不需要魔法,不需要激进的视频,不需要每天24小时的工作。一名优秀的管理者只需要确保他的员工能够工作,并且他必须关心他们。

    草率行事

    怎样才能确保员工能够工作呢?即使向最有思想的管理者问这个问题,他们也会说,“消除障碍物。”那很显然,不是吗?但是再问一下,“什么样的障碍物呢?”然后他们会说,“追踪到各个依赖方,”“追着项目经理要规范书,”或者“要求测试者提供象样的重现步骤。”真是太误导了!

    为什么对跨部门障碍的正面攻击如此被误导呢?

    • 当管理者试图成为英雄的时候,他们只会像白痴一样去管理。他们追踪到依赖方,结果就是得到匆匆忙忙、价值不高的交付成果,而不是在“建造验证测试”(BVT,Build Verification Test)和应用程序编程接口设计方面开展合作,从一开始就考虑到各种可能性、制定切实可行的计划。他们追着项目经理要规范书,而不是在设计的各个方面开展合作。他们要求测试者提供象样的重现步骤,而不是进行源代码插桩,以便对于任何原因引起的故障轻易就能进行调试。换句话说,被误导的管理者他们本身就是问题的一部分,而不是解决方案的一部分。
    • 跨部门障碍是难以消除的,而且很费时间。管理者在开始勇敢探险之前,需要给他们的团队找到迂回措施。
    • 被误导的管理者允许跨部门障碍转移他们的注意力,他们忽略了其他更为直接、基本的利害关系。

    当然,解决跨部门问题也是重要的。但通常有更为简单的方法去帮助解决或绕开问题,况且还有更多基本的问题需要去优先处理。

    作者注:我还要指出,新任管理者更关注在消除复杂的障碍,而不是那些简单的(下面我会谈到),这是他们最常犯的错误之一。新任管理者另一个最常犯的错误是,他们仍然坚持单兵作战,而不是把自己当成团队的代表,他们没有恰当地把工作委托出去。(参见第8章的“时间够用了”栏目。)

    我想要工作

    那么,为了确保员工能够工作,管理者应该提供哪些基本的必需品呢?正如我刚才所说的那样,那很容易。工程师们不需要很多,他们只要:

    • 一张装有电话的办公桌
    • 电力供应(照明、取暖、电流)
    • 一个带有键盘和显示器的电脑(有些人甚至不需要鼠标)
    • 网络访问权限
    • 一个健康的环境(安全、相对安静、能够呼吸的空气)
    • 工作任务

    谁还能把这个搞糟了?也许给员工找到工作任务要复杂一点,但通常那不成问题。然而,有许多管理者能够容忍员工几天都没有电脑——管理者在一位新的团队成员加入之时,还没有为他准备好办公用品。

    最近一次当你的网络瘫痪或者周围噪音太高的时候,有什么事情发生吗?你的管理者有没有放下手中的所有事情,尽他所能去解决那个问题?如果没有,那他作为管理者绝对是不称职的。没什么比保证你的员工能够工作更为基本的事情了。

    那什么叫提供一个安全的环境呢?你的团队中有敌对状况吗?你的管理者对此做了些什么?这也是进行必要的“抗骚扰”培训的原因。确保所有人都有机会在一个安全的环境中工作,没什么比那还要更关键、更重要的了。

    我不是东西

    优秀管理者必须做的第二件简单的事情是关心他的员工。这并不意味着热情的拥抱或贺卡。你甚至不需要喜欢他们。你只需要关心你的员工,客观、真实地看待他们——他们是人类中的一员。

    可能又有人会问,谁还能把这个搞糟了?那能有多难啊?然而,管理者常常把他们的员工看成是“资源”,而不是“人”。他们给员工打上“好”或“坏”的标签。他们把员工看成是东西,而不把他们当人看。

    难道管理者没有亲信吗?那也会带来伤害,不是吗——即使你是亲信中的一员?那是因为你稍不小心就会失宠。管理者如果重用亲信,那他也不再把他的员工当人看了,在他眼里,员工成了收藏品。

    花点时间去了解和欣赏你的员工,把他们真正地当人看待,这其实不难,更多是一个承诺。你必须抛开你自己的喜好和偏见,让其他人了解你,从而你也能了解他们。我意识到这有些多愁善感,不过优秀的管理者的确把他们的员工当作真正的人来尊重,他们不把员工看成是抽象的人头数。

    顺便说一下,你不必刻意说什么或做什么以显示你的关心。实际上,如果你不是真的关心别人的话,哪怕再多的言语或行动都无法使他们信服。人们很容易就能自己判断出来。你可以对他们大叫、表扬他们、批评他们、甚至使他们失望——只要你尊重员工,他们都能理解所有那一切,并且仍然尊重你。

    记住,为了成为一名优秀的管理者,所有你要做的就是确保你的人能够工作,并且把他们当人去对待。

    作者注:那是个“充要条件”——意思是说,如果你确保你的人能够工作,并且你关心他们,那你就是一名优秀的管理者(实际上是较好管理者中的一员)。如果你不能确保他们能够工作,或者你不关心他们,那你就是一名糟糕的管理者,哪怕你在其他方面做出了巨大的努力。

    从优秀到卓越

    如果所有的管理者都至少能够做到“优秀”这个程度,那我们公司乃至整个世界都将更加美好。然而,微软是一个带有竞争性的地方,因此你可能想知道怎样才能成为一名卓越的管理者。

    很多图书、杂志和研究所都已经致力于定义卓越管理者。在我看来,归根结底就只有如下的3个方面:

    • 成为一名优秀的管理者。不要从“卓越”起步;先从“优秀”做起。这是基础!如果你把这个基础忘掉了,那你也失去了员工的尊重和效力。
    • 个性正直。这意味着,你的言语和行动要与你的信仰保持一致。如果你相信要有一个严格的工作道德规范,同时也要维持一个清晰的工作与生活的平衡,那就以身作则展示给大家看。如果你相信质量第一,那就优先抓质量。你来设置标准。你来定义团队。
    • 定义清晰的目标、优先级和界限。把你的目标、优先级和界限往上、往下以及在组织内部横向进行清晰的沟通,这是高效团队和个人的本质。目标可以以期望、承诺或远景声明的形式出现。不管采用哪种形式,它们都指出了我们要走向哪里。优先级指出了我们如何到达那里。界限定义了我们可以自由活动的安全边界。如果没有目标,我们可能哪儿也到不了。如果没有优先级,我们很容易就会迷失。如果没有界限,我们一不小心就会绊倒。

    成为一名卓越的管理者是有难度的。你必须扛得住对你的信仰妥协的压力。你必须清晰而有效地传递一个一致的信息,说明你的远景、特别是你不可接受的东西。你必须做所有的这些事情,同时不丢掉对你的员工的人性关怀,还要留意到为了保持他们的生产力而必要的一些细小(但很重要)的东西。

    我服务于人

    那就是成为一名优秀乃至卓越的管理者所要做的一切了。它归根到底就是服务。你不再是那个做实际工作的人了。取而代之,你要使你自己成为你团队成员的仆人,你的目标是让他们都能够成功。如果做得好的话,管理是无私的。这就是最高境界!

    展开全文
  • 设计模式学习之访问者模式

    万次阅读 多人点赞 2017-04-23 22:15:52
    访问者模式是种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用...

    访问者模式,是行为型设计模式之一。访问者模式是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用它了。

    访问者模式的基本想法是,软件系统中拥有一个由许多对象构成的、比较稳定的对象结构,这些对象的类都拥有一个 accept 方法用来接受访问者对象的访问。访问者是一个接口,它拥有一个 visit 方法,这个方法对访问到的对象结构中不同类型的元素做出不同的处理。在对象结构的一次访问过程中,我们遍历整个对象结构,对每一个元素都实施 accept 方法,在每一个元素的 accept 方法中会调用访问者的 visit 方法,从而使访问者得以处理对象结构的每一个元素,我们可以针对对象结构设计不同的访问者类来完成不同的操作,达到区别对待的效果。

    定义及使用场景

    定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。

    可以对定义这么理解:有这么一个操作,它是作用于一些元素之上的,而这些元素属于某一个对象结构。同时这个操作是在不改变各元素类的前提下,在这个前提下定义新操作是访问者模式精髓中的精髓。

    使用场景:
    (1)对象结构比较稳定,但经常需要在此对象结构上定义新的操作。

    (2)需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

    UML图

    这里写图片描述

    (1)Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。

    (2)ConcreteVisitor1、ConcreteVisitor2:具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。

    (3)Element:元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。

    (4)ConcreteElementA、ConcreteElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

    (5)ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。

    访问者模式的简单例子

    我们都知道财务都是有账本的,这个账本就可以作为一个对象结构,而它其中的元素有两种,收入和支出,这满足我们访问者模式的要求,即元素的个数是稳定的,因为账本中的元素只能是收入和支出。

    而查看账本的人可能有这样几种,比如老板,会计事务所的注会,财务主管,等等。而这些人在看账本的时候显然目的和行为是不同的。

    首先我们给出单子的接口,它只有一个方法accept。

    //单个单子的接口(相当于Element)
    public interface Bill {
    
        void accept(AccountBookViewer viewer);
        
    }
    

    其中的方法参数AccountBookViewer是一个账本访问者接口,接下来也就是实现类,收入单子和消费单子,或者说收入和支出类。

    //消费的单子
    public class ConsumeBill implements Bill{
    
        private double amount;
        
        private String item;
        
        public ConsumeBill(double amount, String item) {
            super();
            this.amount = amount;
            this.item = item;
        }
    
        public void accept(AccountBookViewer viewer) {
            viewer.view(this);
        }
    
        public double getAmount() {
            return amount;
        }
    
        public String getItem() {
            return item;
        }
    
    }
    
    //收入单子
    public class IncomeBill implements Bill{
    
        private double amount;
        
        private String item;
        
        public IncomeBill(double amount, String item) {
            super();
            this.amount = amount;
            this.item = item;
        }
        
        public void accept(AccountBookViewer viewer) {
            viewer.view(this);
        }
    
        public double getAmount() {
            return amount;
        }
    
        public String getItem() {
            return item;
        }
    
    }
    

    上面最关键的还是里面的accept方法,它直接让访问者访问自己,这相当于一次静态分派(文章最后进行解释),当然我们也可以不使用重载而直接给方法不同的名称。

    接下来是账本访问者接口

    //账单查看者接口(相当于Visitor)
    public interface AccountBookViewer {
    
        //查看消费的单子
        void view(ConsumeBill bill);
        
        //查看收入的单子
        void view(IncomeBill bill);
        
    }
    

    这两个方法是重载方法,就是在上面的元素类当中用到的,当然你也可以按照访问者模式类图当中的方式去做,将两个方法分别命名为viewConsumeBill和viewIncomeBill,而一般建议按照类图上来做的

    访问者的实现

    //老板类,查看账本的类之一
    public class Boss implements AccountBookViewer{
        
        private double totalIncome;
        
        private double totalConsume;
        
        //老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心
        public void view(ConsumeBill bill) {
            totalConsume += bill.getAmount();
        }
    
        public void view(IncomeBill bill) {
            totalIncome += bill.getAmount();
        }
    
        public double getTotalIncome() {
            System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
            return totalIncome;
        }
    
        public double getTotalConsume() {
            System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
            return totalConsume;
        }
        
    }
    
    //注册会计师类,查看账本的类之一
    public class CPA implements AccountBookViewer{
    
        //注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
        public void view(ConsumeBill bill) {
            if (bill.getItem().equals("工资")) {
                System.out.println("注会查看工资是否交个人所得税。");
            }
        }
        //如果是收入,则所有的收入都要交税
        public void view(IncomeBill bill) {
            System.out.println("注会查看收入交税了没。");
        }
    
    }
    

    老板只关心收入和支出的总额,而注会只关注该交税的是否交税

    接下来是账本类,它是当前访问者模式例子中的对象结构

    //账本类(相当于ObjectStruture)
    public class AccountBook {
        //单子列表
        private List<Bill> billList = new ArrayList<Bill>();
        //添加单子
        public void addBill(Bill bill){
            billList.add(bill);
        }
        //供账本的查看者查看账本
        public void show(AccountBookViewer viewer){
            for (Bill bill : billList) {
                bill.accept(viewer);
            }
        }
    }
    

    账本类当中有一个列表,这个列表是元素(Bill)的集合,这便是对象结构的通常表示,它一般会是一堆元素的集合,不过这个集合不一定是列表,也可能是树,链表等等任何数据结构,甚至是若干个数据结构。其中show方法,就是账本类的精髓,它会枚举每一个元素,让访问者访问。

    测试客户端

    public class Client {
    
        public static void main(String[] args) {
            AccountBook accountBook = new AccountBook();
            //添加两条收入
            accountBook.addBill(new IncomeBill(10000, "卖商品"));
            accountBook.addBill(new IncomeBill(12000, "卖广告位"));
            //添加两条支出
            accountBook.addBill(new ConsumeBill(1000, "工资"));
            accountBook.addBill(new ConsumeBill(2000, "材料费"));
            
            AccountBookViewer boss = new Boss();
            AccountBookViewer cpa = new CPA();
            
            //两个访问者分别访问账本
            accountBook.show(cpa);
            accountBook.show(boss);
            
            ((Boss) boss).getTotalConsume();
            ((Boss) boss).getTotalIncome();
        }
    }
    

    上面的代码中,可以这么理解,账本以及账本中的元素是非常稳定的,这些几乎不可能改变,而最容易改变的就是访问者这部分。

    访问者模式最大的优点就是增加访问者非常容易,我们从代码上来看,如果要增加一个访问者,你只需要做一件事即可,那就是写一个类,实现AccountBookViewer接口,然后就可以直接调用AccountBook的show方法去访问账本了。

    如果没使用访问者模式,一定会增加许多if else,而且每增加一个访问者,你都需要改你的if else,代码会显得非常臃肿,而且非常难以扩展和维护。

    静态分派以及动态分派

    变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type);而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)。比如:

    List list = null;
    list = new ArrayList();
    

    声明了一个变量list,它的静态类型(也叫明显类型)是List,而它的实际类型是ArrayList。根据对象的类型而对方法进行的选择,就是分派(Dispatch),分派(Dispatch)又分为两种,即静态分派和动态分派。静态分派(Static Dispatch)发生在编译时期,分派根据静态类型信息发生。

    静态分派
    静态分派就是按照变量的静态类型进行分派,从而确定方法的执行版本,静态分派在编译时期就可以确定方法的版本。而静态分派最典型的应用就是方法重载

    public class Main {
    
        public void test(String string){
            System.out.println("string");
        }
        
        public void test(Integer integer){
            System.out.println("integer");
        }
        
        public static void main(String[] args) {
            String string = "1";
            Integer integer = 1;
            Main main = new Main();
            main.test(integer);
            main.test(string);
        }
    }
    

    在静态分派判断的时候,我们根据多个判断依据(即参数类型和个数)判断出了方法的版本,那么这个就是多分派的概念,因为我们有一个以上的考量标准,也可以称为宗量。所以JAVA是静态多分派的语言。

    动态分派
    对于动态分派,与静态相反,它不是在编译期确定的方法版本,而是在运行时才能确定。而动态分派最典型的应用就是多态的特性

    interface Person{
        void test();
    }
    class Man implements Person{
        public void test(){
            System.out.println("男人");
        }
    }
    class Woman implements Person{
        public void test(){
            System.out.println("女人");
        }
    }
    public class Main {
        
        public static void main(String[] args) {
            Person man = new Man();
            Person woman = new Woman();
            man.test();
            woman.test();
        }
    }
    

    这段程序输出结果为依次打印男人和女人,然而这里的test方法版本,就无法根据man和woman的静态类型去判断了,他们的静态类型都是Person接口,根本无从判断。

    显然,产生的输出结果,就是因为test方法的版本是在运行时判断的,这就是动态分派。

    动态分派判断的方法是在运行时获取到man和woman的实际引用类型,再确定方法的版本,而由于此时判断的依据只是实际引用类型,只有一个判断依据,所以这就是单分派的概念,这时我们的考量标准只有一个宗量,即变量的实际引用类型。相应的,这说明JAVA是动态单分派的语言。

    访问者模式中的伪动态双分派

    访问者模式中使用的是伪动态双分派,所谓的动态双分派就是在运行时依据两个实际类型去判断一个方法的运行行为,而访问者模式实现的手段是进行了两次动态单分派来达到这个效果。

    回到上面例子当中账本类中的accept方法

    for (Bill bill : billList) {
                bill.accept(viewer);
            }
    

    这里就是依据biil和viewer两个实际类型决定了view方法的版本,从而决定了accept方法的动作。

    分析accept方法的调用过程
    1.当调用accept方法时,根据bill的实际类型决定是调用ConsumeBill还是IncomeBill的accept方法。

    2.这时accept方法的版本已经确定,假如是ConsumeBill,它的accept方法是调用下面这行代码。

     public void accept(AccountBookViewer viewer) {
            viewer.view(this);
        }
    

    此时的this是ConsumeBill类型,所以对应于AccountBookViewer接口的view(ConsumeBill bill)方法,此时需要再根据viewer的实际类型确定view方法的版本,如此一来,就完成了动态双分派的过程。

    以上的过程就是通过两次动态双分派,第一次对accept方法进行动态分派,第二次对view(类图中的visit方法)方法进行动态分派,从而达到了根据两个实际类型确定一个方法的行为的效果。

    而原本我们的做法,通常是传入一个接口,直接使用该接口的方法,此为动态单分派,就像策略模式一样。在这里,show方法传入的viewer接口并不是直接调用自己的view方法,而是通过bill的实际类型先动态分派一次,然后在分派后确定的方法版本里再进行自己的动态分派。

    注意:这里确定view(ConsumeBill bill)方法是静态分派决定的,所以这个并不在此次动态双分派的范畴内,而且静态分派是在编译期就完成的,所以view(ConsumeBill bill)方法的静态分派与访问者模式的动态双分派并没有任何关系。动态双分派说到底还是动态分派,是在运行时发生的,它与静态分派有着本质上的区别,不可以说一次动态分派加一次静态分派就是动态双分派,而且访问者模式的双分派本身也是另有所指。

    这里的this的类型不是动态确定的,你写在哪个类当中,它的静态类型就是哪个类,这是在编译期就确定的,不确定的是它的实际类型,请各位区分开这一点。

    对访问者模式的一些思考

    假设我们上面的例子当中再添加一个财务主管,而财务主管不管你是支出还是收入,都要详细的查看你的单子的项目以及金额,简单点说就是财务主管类的两个view方法的代码是一样的。

    这里的将两个view方法抽取的方案是,我们可以将元素提炼出层次结构,针对层次结构提供操作的方法,这样就实现了优点当中最后两点提到的针对层次定义操作以及跨越层次定义操作。

    //单个单子的接口(相当于Element)
    public interface Bill {
    
        void accept(Viewer viewer);
        
    }
    
    //抽象单子类,一个高层次的单子抽象
    public abstract class AbstractBill implements Bill{
        
        protected double amount;
        
        protected String item;
        
        public AbstractBill(double amount, String item) {
            super();
            this.amount = amount;
            this.item = item;
        }
        
        public double getAmount() {
            return amount;
        }
    
        public String getItem() {
            return item;
        }
        
    }
    
    //收入单子
    public class IncomeBill extends AbstractBill{
        
        public IncomeBill(double amount, String item) {
            super(amount, item);
        }
    
        public void accept(Viewer viewer) {
            if (viewer instanceof AbstractViewer) {
                ((AbstractViewer)viewer).viewIncomeBill(this);
                return;
            }
            viewer.viewAbstractBill(this);
        }
    
    }
    
    //消费的单子
    public class ConsumeBill extends AbstractBill{
    
        public ConsumeBill(double amount, String item) {
            super(amount, item);
        }
    
        public void accept(Viewer viewer) {
            if (viewer instanceof AbstractViewer) {
                ((AbstractViewer)viewer).viewConsumeBill(this);
                return;
            }
            viewer.viewAbstractBill(this);
        }
    
    }
    

    这是元素类的层次结构,可以看到,我们的accept当中出现了if判断,这里的判断是在判断一个层次,这段代码是不会被更改的。

    访问者层次

    //超级访问者接口(它支持定义高层操作)
    public interface Viewer{
    
        void viewAbstractBill(AbstractBill bill);
        
    }
    
    //比Viewer接口低一个层次的访问者接口
    public abstract class AbstractViewer implements Viewer{
    
        //查看消费的单子
        abstract void viewConsumeBill(ConsumeBill bill);
        
        //查看收入的单子
        abstract void viewIncomeBill(IncomeBill bill);
        
        public final void viewAbstractBill(AbstractBill bill){}
    }
    
    //老板类,查看账本的类之一,作用于最低层次结构
    public class Boss extends AbstractViewer{
        
        private double totalIncome;
        
        private double totalConsume;
        
        //老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心
        public void viewConsumeBill(ConsumeBill bill) {
            totalConsume += bill.getAmount();
        }
    
        public void viewIncomeBill(IncomeBill bill) {
            totalIncome += bill.getAmount();
        }
    
        public double getTotalIncome() {
            System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
            return totalIncome;
        }
    
        public double getTotalConsume() {
            System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
            return totalConsume;
        }
        
    }
    
    //注册会计师类,查看账本的类之一,作用于最低层次结构
    public class CPA extends AbstractViewer{
    
        //注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
        public void viewConsumeBill(ConsumeBill bill) {
            if (bill.getItem().equals("工资")) {
                System.out.println("注会查看是否交个人所得税。");
            }
        }
        //如果是收入,则所有的收入都要交税
        public void viewIncomeBill(IncomeBill bill) {
            System.out.println("注会查看收入交税了没。");
        }
    
    }
    
    //财务主管类,查看账本的类之一,作用于高层的层次结构
    public class CFO implements Viewer {
    
        //财务主管对每一个单子都要核对项目和金额
        public void viewAbstractBill(AbstractBill bill) {
            System.out.println("财务主管查看账本时,每一个都核对项目和金额,金额是" + bill.getAmount() + ",项目是" + bill.getItem());
        }
    
    }
    

    财务主管(CFO)是针对AbstractBill这一层定义的操作,而原来的老板(Boss)和注册会计师(CPA)都是针对ConsumeBill和IncomeBill这一层定义的操作,这时已经产生了跨越层次结构的行为,老板和注册会计师都跨过了抽象单子这一层,直接针对具体的单子定义操作。

    账本类没有变化,最后看客户端的使用

    public class Client {
    
        public static void main(String[] args) {
            AccountBook accountBook = new AccountBook();
            //添加两条收入
            accountBook.addBill(new IncomeBill(10000, "卖商品"));
            accountBook.addBill(new IncomeBill(12000, "卖广告位"));
            //添加两条支出
            accountBook.addBill(new ConsumeBill(1000, "工资"));
            accountBook.addBill(new ConsumeBill(2000, "材料费"));
            
            Viewer boss = new Boss();
            Viewer cpa = new CPA();
            Viewer cfo = new CFO();
            
            //两个访问者分别访问账本
            accountBook.show(cpa);
            accountBook.show(boss);
            accountBook.show(cfo);
            
            ((Boss) boss).getTotalConsume();
            ((Boss) boss).getTotalIncome();
        }
    }
    

    回想一下,要是再出现和财务主管一样对所有单子都是一样操作的人,我们就不需要复制代码了,只需要让他实现Viewer接口就可以了,而如果要像老板和注会一样区分单子的具体类型,则继承AbstractViewer就可以。

    Android中的访问者模式

    安卓中的著名开源库ButterKnife、Dagger、Retrofit都是基于APT(Annotation Processing Tools)实现。而编译注解核心依赖APT。当我们通过APT处理注解时,最终会将获取到的元素转换为相应的Element元素,以便获取到它们对应信息。那么元素基类的源码如下:(路径:javax.lang.model.element.Element)

    public interface Element extends javax.lang.model.AnnotatedConstruct {
    
        /**
         * Returns the {@code kind} of this element.
         *
         * @return the kind of this element
         */
        ElementKind getKind();//获取元素类型
    
        //代码省略
    
        /**
         * Applies a visitor to this element.
         *
         * @param <R> the return type of the visitor's methods
         * @param <P> the type of the additional parameter to the visitor's methods
         * @param v   the visitor operating on this element
         * @param p   additional parameter to the visitor
         * @return a visitor-specified result
         */
        <R, P> R accept(ElementVisitor<R, P> v, P p);//接受访问者的访问
    }
    

    ElementVisitor就是访问者类型,ElementVisitor源码如下:

    public interface ElementVisitor<R, P> {
        /**
         * Visits an element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visit(Element e, P p);
    
        /**
         * A convenience method equivalent to {@code v.visit(e, null)}.
         * @param e  the element to visit
         * @return a visitor-specified result
         */
        R visit(Element e);
    
        /**
         * Visits a package element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitPackage(PackageElement e, P p);
    
        /**
         * Visits a type element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitType(TypeElement e, P p);
    
        /**
         * Visits a variable element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitVariable(VariableElement e, P p);
    
        /**
         * Visits an executable element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitExecutable(ExecutableElement e, P p);
    
        /**
         * Visits a type parameter element.
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         */
        R visitTypeParameter(TypeParameterElement e, P p);
    
        /**
         * Visits an unknown kind of element.
         * This can occur if the language evolves and new kinds
         * of elements are added to the {@code Element} hierarchy.
         *
         * @param e  the element to visit
         * @param p  a visitor-specified parameter
         * @return a visitor-specified result
         * @throws UnknownElementException
         *  a visitor implementation may optionally throw this exception
         */
        R visitUnknown(Element e, P p);
    }
    

    在ElementVisitor中定义了多种visit接口,每个接口处理一种元素类型,那么这就是典型的访问者模式。

    总结

    优点:

    1、使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。

    2、添加新的操作或者说访问者会非常容易。

    3、将对各个元素的一组操作集中在一个访问者类当中。

    4、使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。

    5、可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。

    缺点:

    1、增加新的元素会非常困难。

    2、实现起来比较复杂,会增加系统的复杂性。

    3、破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构,就像收入和支出类必须提供访问金额和单子的项目的方法一样。

    适用性:

    1、数据结构稳定,作用于数据结构的操作经常变化的时候。

    2、当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式。

    3、有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。

    最后,推送一下自己的微信公众号,喜欢的同学可以关注。
    在这里插入图片描述

    展开全文
  • 访问ftp服务器文件夹

    千次阅读 2021-08-11 06:48:39
    访问ftp服务器文件夹 内容精选换换通过Web浏览器支持批量登录主机,提供文件传输、文件管理和预置命令等功能。用户在主机上执行的所有操作,被云堡垒机记录并生成审计数据。本小节主要介绍如何通过Web浏览器批量...
  • 导航:网站首页 >...匿名网友:参考答案: 755表示该文件所有者对该文件具有读、写、执行权限,该文件所有所在组用户及其他用户该文件具有读和执行权限。试题难度:★★☆参考解析: 暂无解析匿名网...
  • 我们在使用Win7系统的时候,有时间会遇到在删除或编辑某个文件或者文件夹时,出现“您的权限不足,请点击继续来获得权限”、“您当前无权访问该文件夹”的提示信息。这是因为Win7系统的默认账户虽然是管理员账户,...
  • 第七章:访问控制技术原理和应用

    千次阅读 2021-01-19 18:27:45
    访问控制概述 主要讲了什么是访问...资源对象的访问者授权、控制的方法和运行机制 访问控制模型 访问控制类型 访问控制策略和实现 访问控制过程与安全管理 访问控制主要产品与技术指标 访问控制技术应用 ...
  • xwiki管理指南-访问权限

    千次阅读 多人点赞 2018-05-21 09:49:58
    从XWiki 5.0版本开始,我们使用新的安全模块。虽然与之前的权限几乎兼容,但是还是有一些细微的差别。...你可以设置个用户只在个空间编辑页面的权限,通过禁止用户访问全局wiki并在该空间授予用户编辑权限。 当...
  • 基于角色管理的系统访问控制

    千次阅读 2013-10-09 14:08:03
    引言(Introduction) 1.1. 关键词定义(Definitions) 有关定义说明如下: 安全管理:计算机技术安全管理的范围很广,可以...本文中有关关涉及"安全管理"词均只针对本公司推出的应用中有关对象与数据而言范围有限
  • 个典型的软件开发场景中,你作为名开发人员加入到某个项目后,假设是“超人组”,你往往需要访问这个项目的代码库然后才能开始工作。当你的 Team Lead 将你加入 Git Organization 后,你自然可以访问到“超人...
  • 失效的访问控制

    千次阅读 2018-08-16 11:25:05
    失效的访问控制 出现的问题 文件包含/目录遍历 权限绕过(越权) 权限提升(提权) ...这是我在OWASP上几篇文章翻译整理加删减总结出的个关于失效的访问控制的检查可解决的文章。勉强算个原创...
  • 个基于SpringBoot+vue的学生信息管理系统详细设计

    万次阅读 多人点赞 2021-11-30 23:23:29
    主要功能:用户登录、用户管理、角色、菜单、灵活控制、sql监控、日志、老师、学生、班级、课程、选课、打分、成绩、新闻、通知公告、附件查看下载等功能
  • windows访问控制列表ACL

    千次阅读 2019-07-15 12:56:32
    文章目录术语定义分类DACLSACL安全对象(so) &...windows访问控制列表 --ACL(Access Control List) 关于授权(authorization) 与 访问控制(Acess Control) 微软官网详细介绍: https://docs.microsoft.com/zh-cn...
  • 使用阿里云的 OSS 的客户端 SDK上传了个文件,访问存储地址时遇到如下报错信息: <Error> <Code>AccessDenied</Code> <Message>You have no right to access this object because of ...
  • 数据仓库设计 网站/APP 流量分析、用户访问分析 网站/APP 流量分析、点击流分析、用户访问分析 网站埋点+网站日志自定义采集系统... 用户网站的每次访问包含了系列的点击动作行为,这些点击行为数据就构成了...
  • 常用的端口及未授权访问漏洞

    千次阅读 2022-02-15 15:17:55
    、介绍 用于快速定位端口,查找开放端口脆弱点,有利于外网渗透 1.1 具体端口 端口号 使用 弱点 21 telnet 22 SSH 28退格漏洞、OpenSSL漏洞 25 SMTP协议 53 DNS服务 67&68 DHCP服务 ...
  • 前几天给电脑重新做了个系统,然后在E盘清理文件的时候突然发现有个文件夹删不掉,没有权限,如图:点管理员权限继续也是不得行。 于是乎我试图删除里面的单个文件,结果可想而知也是没有权限。如图: 我又试图...
  • 它将数据的操作与数据结构进行分离,是行为类模式中最复杂的种模式。 优点: ①扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。 ②复用性好。可以通过访问者来定义整个对象...
  • 第25章 访问者模式

    2020-03-18 15:43:47
    小兵的情况不是他要了解的,就像战争时期一位将军说:“我一想到我的士兵也有孩子、妻子、父母,我就痛心疾首……但是这是战场,我只能认为他们是一群机器……”是啊,其实我们也一样啊,那问题就出来了: ● 大老板...
  • 42.admin后台管理的使用及基本设置

    千次阅读 多人点赞 2021-09-09 21:18:36
    在项目目录下的urls.py文件中我们会看到这样个url的配置,你有自己去访问这个url地址吗?? 访问之后发现了什么呢??? 访问127.0.0.1:8000/admin之后, 你会看到出来这样个界面, 让你输入用户名和密码去登录? 那么...
  • 教学管理系统类图

    万次阅读 多人点赞 2018-05-16 22:07:13
     文字说明 用户类的整体说明:使用该系统的参与被称为用户,拥有登录的权限属性说明()账号:具有一定格式的唯一标识每个用户的字符串;(二)姓名:使用的姓名;(三)密码:验证用户所需要的字符串。操作...
  • 几周前,我们新开了系列文章,旨在深入 JavaScript,探寻其工作原理。我们认为通过了解 JavaScript 的构建方式和其运行规则,我们能写出更好的代码和应用。 第篇文章重点介绍了引擎、运行时和调用栈的概述。...
  • 基于身份的加密体制可以看作种特殊的公钥加密,它有如下特点:系统中用户的公钥可以由任意的字符串组成。这些字符串可以是用户在现实中的身份信息,如:身份证号码、用户姓名、电话号码、Email地址等,因为用户的...
  • 局域网两个不同网段互相访问

    千次阅读 2021-04-07 10:15:36
    局域网内为便于管理和接入,经常划分为不同的网段,不同网段的PC通过个路由器与上层网段连接,可访问外网或上层网络;但上层或同层网络不同网段PC不能访问其他网段PC。 PC通过个路由器与主网或上层网络连接时,...
  • 属性加密技术及基于属性的访问控制技术

    千次阅读 多人点赞 2020-04-07 13:37:10
    、属性加密技术... 1 1.1基于身份的加密体制简介... 1 1.2基于属性的加密体制的研究背景和意义... 1 1.3基于属性加密的研究现状... 2 二、基于属性的访问控制技术... 5 2.1基于属性的访问控制介绍... 5 2.2...
  • 解决打不开SQL Server配置管理器的问题

    万次阅读 多人点赞 2018-08-31 10:58:00
    最近被Sql Server搞得贼烦,下了俩次SQL SERVER,重装了次系统,先这次遇到的问题发一下感慨:深深地意识到权限的重要性了,一般计算机里面的软件都有不同的访问权限,普通用户(Users)、管理员...
  • 一般我们可以通过安全管理器机制来完善安全性,安全管理器是安全的实施,可安全管理器进行扩展,它提供了施加在应用程序上的安全措施,通过配置安全策略文件,达到网络、本地文件和程序其他部分的访问加以限制...
  • Linux系统管理---linux用户管理

    万次阅读 2022-02-10 09:21:53
    、配置文件 1、/etc/password 2、/etc/shadow 时间戳转日期: 3、/etc/group 4、/etc/gshadow 5、/etc/login.defs 6、/etc/default/useradd 7、/etc/skel 二、命令 1、用户创建过程 useradd/usermod/...
  • 然后最近在帮个朋友做管理系统,他想用C#做后台,好不容易本地测试localhost可以用,但是发现用花生壳测试内网映射时候写本机的ip地址或者直接写127.0.0.1不能正常访问。网上查了一下,试了很多解决办法都没能...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 289,662
精华内容 115,864
热门标签
关键字:

对一位管理者的访问