Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:90359 Return-Path: <mdwheele@ncsu.edu> Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 50103 invoked from network); 8 Jan 2016 16:05:06 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 8 Jan 2016 16:05:06 -0000 Authentication-Results: pb1.pair.com smtp.mail=mdwheele@ncsu.edu; spf=permerror; sender-id=unknown Authentication-Results: pb1.pair.com header.from=mdwheele@ncsu.edu; sender-id=unknown Received-SPF: error (pb1.pair.com: domain ncsu.edu from 209.85.217.181 cause and error) X-PHP-List-Original-Sender: mdwheele@ncsu.edu X-Host-Fingerprint: 209.85.217.181 mail-lb0-f181.google.com Received: from [209.85.217.181] ([209.85.217.181:36389] helo=mail-lb0-f181.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 73/6E-55593-23EDF865 for <internals@lists.php.net>; Fri, 08 Jan 2016 11:05:06 -0500 Received: by mail-lb0-f181.google.com with SMTP id oh2so227075962lbb.3 for <internals@lists.php.net>; Fri, 08 Jan 2016 08:05:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ncsu-edu.20150623.gappssmtp.com; s=20150623; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=ZDHFlEWDfsUvvWzoNcBjdfNfoUtnaT444QPiaM5GB1U=; b=ejWpaLmNx5xoAyj28dVSNdO3kqAIX4RMQhgCkikuN0v7zjahCGG+WWmGS6d97fhe9v mqJAuxIMm1Tl9p3lj3ligjcKBmpMJaHtXel7b2Z//5CvJ15eHr6LNMvmYhPszyIXIRfR MD6MqbC6rPAEtQ9g+PlLjpld+ZnbMYI5vZ4kGffliVdaTPMrhCTw0r1v7ZlQuzg+PiPO mVmS/nE2WZ7/VVgEmn3/mrO+pcnFyTT+kfWlrFeGlrAfwkrv6RnvoMw4BV+stHzEGgKC E4+qBpjZKfhqwQc/GNjSPvXsoPIeTppPR1AkojCksiGZ/hcVmTvBd0zJSLjMI7yMHPYe fv9Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; bh=ZDHFlEWDfsUvvWzoNcBjdfNfoUtnaT444QPiaM5GB1U=; b=CagD7zcQYuHbtCivhYWNlqOSfnar4T/5QFDh/bg7l4ccARKXH/7WKSHSZLAskcPG8u iMmxfhUTkCs3tnnu1PzFsZFN6fAdKN+PD8QqrydZxwLiIqZ11lDiVyySwX6zDY5j8YKd v++dj6VhtlASorEVZRzqyjdpbFTvCtQvMkQIrLe0uhNv/wegIrn0l8lBS+JZ8YoEVEuW USJvQF32B38HRppvo2VVY6N3Gtd3L12M5C1l7EmHb2VmS8SHnnHVKjslbcRwglx2en6H +9wXr4BVy1Bgmc+NSQYNUCNTWkWckdBT9daab++vo8NjQGP9Og5rrfKSdJbqzdBVIOTD xldA== X-Gm-Message-State: ALoCoQkBFdLvZXAYwyjZRGsILYFmqTFezYxQUW76vum227LX3+fltaq3S7y+gtSLUw/3w7Q7y2n4V8M9woCPK1zS9ZsfbFLsnnSP9Qldq/v7eF9SDHK/03s= MIME-Version: 1.0 X-Received: by 10.112.147.161 with SMTP id tl1mr2771881lbb.4.1452269103442; Fri, 08 Jan 2016 08:05:03 -0800 (PST) Received: by 10.112.108.198 with HTTP; Fri, 8 Jan 2016 08:05:03 -0800 (PST) In-Reply-To: <CA+kxMuR4kcDYMAiYjFPp0+anFRgYHiFbxmZcG2-x0yAGttiP-w@mail.gmail.com> References: <CADMz3wco4JNLHQV+NvnaA5npF=hTnzp0zdB3gK+oDbL6tTDuFA@mail.gmail.com> <CA+kxMuR4kcDYMAiYjFPp0+anFRgYHiFbxmZcG2-x0yAGttiP-w@mail.gmail.com> Date: Fri, 8 Jan 2016 11:05:03 -0500 Message-ID: <CADMz3wea8fvTyR7n=Per+G0aGLxFkdHYAboNdMwaqOG9zg+uXA@mail.gmail.com> To: Dan Ackroyd <danack@basereality.com> Cc: PHP internals <internals@lists.php.net> Content-Type: text/plain; charset=UTF-8 Subject: Re: [PHP-DEV] [RFC] Class Friendship From: mdwheele@ncsu.edu (Dustin Wheeler) Hi Dan, On Fri, Jan 8, 2016 at 9:56 AM, Dan Ackroyd <danack@basereality.com> wrote: > > Hi Dustin, > > I try to avoid sounding too negative when people come up with ideas > that I might not agree with. However....I feel obliged to give honest > feedback earlier rather than later; I can't see any circumstances > under which I'd vote for this RFC. > No need to hesitate with honest feedback. I understand that to be part of the RFC process. This RFC is already close to "paying for itself" from my perspective, in that it collects critiques and feedback to be attached to the document so that in the event this is declined there is "official" documentation of why. Currently, there are a handful of list archives, chat logs, etc. scattered across the web. This process bakes a more official opinion on the feature. In addition, as part of doing this I learned a ton that I can use to make further contributions myself and to help others get started. Plus, I have an opportunity to go through the process, which is another learning experience! :) > > Although they solve a particular problem, they do so in a way that has > too many downsides. From the examples: > > class Fibonacci { > friend FibonacciTest; > .... > } > > [snip] > > The classes that are exposing their protected properties have to know > about what classes are going to be using them. It means if I want to > add a new test similar to the FibonacciTest, I also have to edit the > Fibonacci class itself.....this is pretty terrible. > I agree that by exposing protected properties to friended classes, you're tightly-coupling an object to another. In the case of white-box testing, friendship is a purposefully introduced smell, in my opinion. Production code marked as coupled to test-cases is 100% a smell, but I see this specific usage as a tactical strategy that is part of a longer-running refactor. In-progress, I might leave these couplings in place, but I would have sniffers that signaled these areas of the project were targets for continued improvement. Your testing example (''IntrospectableFibonacci'') is a great example of alternative solution. I can't argue that friendship offers much over this since we have deviated from the C++ implementation whereby friends have access to ''private'' members. The only benefit I see (currently and for this specific example, which is intentionally trivial) is the class being statically marked for purposes of sniffing / analysis. > > That does not scale well - either in just the amount of code typed, or > in how difficult it is to reason about the code. What ends up > happening is that if a property needs to be exposed to friends once, > it ends up being exposed to lots of friends. > If a class ends up exposed to many friends without real reason semantic to a domain model, my opinion would be that it is a misapplication of the feature. You'd be effectively shooting yourself in the foot with the "tightly-coupled gun". I can make a similar argument against a feature like Traits in that if I over-apply or misapply traits, I am shooting myself in the foot with the "copy-paste and duplication of code gun". I do agree with you that this feature does expose an opportunity for abuse and headache, but I think that can be said about many features in the language. It's hard for me to dictate best practice versus simply the mechanics of the feature. Do you think this type of information is worth baking into the RFC to clarify intended usage of the feature? > > From the other example: > > class HumanResourceReport > { > public function getFullName() { > // HumanResourceReport would not have access to protected > // members of Person if not explicitly listed as a friend. > return $this->person->firstName . ' ' . $this->person->lastName; > } > } > > Again, this has a massive downside. The 'domain logic' of how to > construct a full name from a first name and last name belongs in the > person class. By allowing the individual elements to be accessed, the > code is violating the 'separation of concerns'. > This is really an *overly* simplified example intended to express the mechanics of the feature and not-necessarily "best practice". To a degree, I agree with you that this specific type of presentational logic might belong as part of a single object's API. Over time, as that object grows, I see friendship as an expression of 'separation of concerns', not a violation. Consider CQS (command-query separation), whereby an object's API is constructed by either methods that return observable state free of side-effect or methods that mutate state and do not return anything. As that API grows, it may be appropriate to refactor that model to separate concerns. In many cases, deepened discovery in a domain might grant clarity that you've actually modeled a conflation of ideas. In other cases, you might just have a case where the abstraction *is* correct, but the API serves two concerns (read and write, query and command). In these cases, you grab for friendship as a means of expressing physical separation of concerns of the command and query portions of a given object. Taken further, CQRS (command-query responsibility segregation) expresses totally segregated models for reads and writes across a system. > When middle names are added to the Person object, > the HumanResourcesReport will continue to generate full names as it > has always done....until someone notices a files a bug report. This would be a misstep modeling and failure in testing, in my opinion. Granted, again, that this is an intentionally trivial example so the argument will by extension seem trivial. Keep in mind, that in this example Person might represent solely the business rules and invariants of what it means to be a Person. HumanResourcesReport is a separation of concerns describing what a Person looks like in the domain of human resources. They are truly separate concerns. In this way, I would argue that simply the addition of middle name as an invariant of Person does not imply that it would be incorrect (or a "bug") for the report to continue generating full name as it always had. This is an advantage of friendship, not a failing! Can it be abused? Of course! The tests for HumanResourcesReport should dictate when it's time to change behaviour of that query. Imagine if, instead, Human Resources says, "We like what we see in the report, but we're now required by law to include middle names as part of the full printed name.". We'd write a test for the friended class to add this behaviour and that would influence changes that might (or might not) need to be made on Person. > > Apologies again for being so negative. > No need to apologize! I appreciate the feedback and look forward to continued discussion. Good stuff! Thanks -- Dustin Wheeler | Software Developer NC State University m: mdwheele@ncsu.edu | w: 5-9786 "If you don't know where you're going, it's easy to iteratively not get there."