# 7: Proxies: Defensive and Otherwise ###### tags: `Tag(sp22)` ## Reminder: What We've Seen So Far Recently in 0320's OO design component, we've seen: * The _strategy_ pattern (caller provides a piece of computation, like a `Comparator` to `Collections.sort()`, rather than trusting the called code to know their needs exactly); * the idea of preserving a _canonical_ reference to prevent excessive memory usage; * the value but non-omnipotence of the `private` and `final` field annotations. Today we'll add a few more OO tools. ## Defensive Proxies Here's roughly where we stopped last time: ```java package edu.brown.cs32.livecode.feb17; import java.util.Iterator; import java.util.Map; public class HoursDispatcher { private final Iterator<Student> queue; private final Map<TA, Integer> minutesLeft; private final String statusMessage; HoursDispatcher(Iterator<Student> signups, String statusMessage) { this.queue = signups; this.statusMessage = statusMessage; } void addTA(TA ta, int minutes) { if(minutes <= 0) { throw new CustomBadArgumentException<>("minutes", minutes, "must be greater than 0"); } minutesLeft.put(ta, minutes); } private Map<TA, Integer> public_view_minutesLeft; public Map<TA, Integer> getMinutesLeft() { // Canonical defensive copy if(public_view_minutesLeft == null) public_view_minutesLeft = new HashMap<>(minutesLeft); return public_view_minutesLeft; } } ``` The `getMinutesLeft()` method is set up the same as on Tuesday, and I've also included our validation in `addTA()`, but instead of using `IllegalArgumentExcpetion`, I've added our own custom checked exception: ```java= class CustomBadArgumentException extends Exception { String argname; Object value; // :-( String message; CustomBadArgumentException(String argname, Object value, String message) { this.argname = argname; this.value = value; this.message = message; } } ``` The name could be better, of course. But notice the hack: because you can't define a generic `Exception`, we're forced to fall back on techniques from the Bad Old Days if we want to provide a value we don't know the type of. This is troublesome, and so usually (when I'm not trying to demo a concept) I'd define a more narrow exception, where the type is known. The reason Java forbids generic `Exception` classes is subtle, and I'll only briefly skim it here. Usually, generic type information is not present at runtime: at runtime, you know you've got (say) an `Iterator`, but you don't know if it's an `Iterator<String>` without looking at the data inside. This causes problems if callers would want to catch different generic types differently: `CustomBadArgumentException<String>` vs. `CustomBadArgumentException<Integer>`. #### A Good Practice Whenever you're creating a new class, try to articulate what _properties_ of the class's fields must be guaranteed. In other words, what makes an instance of the class "bad" or "ill-formed". Do post-instantiation and post-modification checks to ensure that your class is well-formed at all times. ### Onward! We were unhappy with this solution because it presented an unchanging view to callers, and that view would also often be inconsistent between different callers. Another concern (I can't recall if anyone mentioned it) is that the caller can modify this canonical public copy, and even if they don't affect the actual `minutesLeft` map, they will affect the view other callers have. So we really ought to be able to do better. ### A Quick Fix: Unmodifiable Maps Java's library provides a useful tool: `Collections.unmodifiableMap`. This static method accepts a `Map` and produces an object that also implements the `Map` interface, but disallows modification. So we could replace the body of `getMinutesLeft()` with: ```java= private Map<TA, Integer> unmodifiable_minutesLeft; public Map<TA, Integer> getMinutesLeft1() { if(unmodifiable_minutesLeft == null) { unmodifiable_minutesLeft = Collections.unmodifiableMap(minutesLeft); } return unmodifiable_minutesLeft; ``` As stated in the [Javadoc for this method](https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html), it: > Returns an unmodifiable view of the specified map. Query operations on the returned map "read through" to the specified map, and attempts to modify the returned map, whether direct or via its collection views, result in an `UnsupportedOperationException`. That is, it creates a new _proxy_ object that restricts what the caller can do. If you look at the source code, you'll see a single-line method body: ```java return new UnmodifiableMap<>(m); ``` And there is, indeed, a static class inside the `Collections` class: ```java private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable { ... } ``` The specifics aren't too important. What _is_ important is that they really are just creating a new class whose sole purpose is _defending_ another `Map` from modification. If you browse the code you can see various ways the authors have carefully considered potential threats. (In fact, skimming the Javadoc for `Collections` is a great idea; there's a lot of goodness there.) When in doubt, return an _unmodifiable_ `Map`. It's quick and easy, and unless you really want the caller to be able to modify the underlying map, is significantly better than a defensive copy alone. We've: * protected the internal `minutesLeft` map from modification; * avoided memory bloat by using a canonical public-facing object; and * ensured that all callers get the same view. Unfortunately: * we've only protected one level of the reference structure: the `TA` keys of the map are still exposed to the caller, and if the `TA` class allows modification, we've just handed that ability over to the caller. * calling this specific method will only work if we want this specific kind of proxy. **Aside:** Again, note that our assumption "the adversary is in the codebase" does need some moderation! If there really is a malicious developer on our team, we can annoy them, and hopefully catch them in code review. But this isn't a security course: we're using the fiction of the adversary to help us think of ways our code could be made safer or more robust. ### Proxy Pattern A _proxy_ class wraps another object and provides all the same interfaces, but might possibly restrict or change the exact behavior of the promised methods. This is an incredibly useful pattern, and it's related to other _structural patterns_ like _decorator_ (which adds in additional methods) or _adapter_ (which modifies one interface into another). There are more than 20 patterns in the [classic book](https://learning.oreilly.com/library/view/design-patterns-elements/0201633612/fm.html), and we won't talk about them all explicitly in class. Calling `Collections.unmodifiableMap` implements the proxy pattern for us, provided we're trying to proxy a `Map` in a very specific way. But what if we want to proxy something else, or proxy in a different way---perhaps to only allow the addition of new `TA` entries, but not modification of old ones? Let's build our own class for that. We'll define it _inside_ the class definition of `HoursDispatcher`---making it an _inner_ class. Instances of inner classes (by default, anyway) are tied to specific instances of the outer class, and get access to all the fields of the outer class. So our `LimitedTATimesMap` below will be able to reference, e.g., `minutesLeft`. Because it provides the `Map` interface, we need to define quite a few methods. Most (like `size()`) are straightforward, and I'll omit almost all of them for brevity. Others, like `remove()`, we want to prohibit entirely, and so they just throw an `UnsupportedOperationException`. For `put()`, we'll make sure that the TA isn't already in the map; if they are, we'll throw the same exception. If not, we'll update the internal map. ```java= class LimitedTATimesMap implements Map<String, Integer> { @Override public int size() { return minutesLeft.size(); } @Override public Integer remove(Object key) { throw new UnsupportedOperationException("remove"); } @Override public Integer put(TA key, Integer value) { if(minutesLeft.containsKey(key)) throw new UnsupportedOperationException("put"); return minutesLeft.put(key, value); } // other methods straightforward or throw exception } ``` ### Another Better Fix: Adapter Pattern Let's try to protect the `TA` objects from modification. One reasonable course would be to create a proxy class for `TA`s. However, what if the caller doesn't really _need_ `TA` objects, but just wants to know the names of all the TAs currently helping students? In that case, we can build an _adapter_ that converts the `Map<TA, Integer>` to a `Collection<String>`. There are many applications for this pattern: any time you have two libraries that need to interact but _don't agree_ on an interface, for instance. This is just one example. The implementation is very similar to the proxy above (and I'll omit many methods that are straightforward): ```java= class OnDutyTAsView implements Collection<String> { @Override public int size() { return minutesLeft.size(); } @Override public Iterator<String> iterator() { // FP approach (concise); can also use an anonymous class return minutesLeft.keySet().stream().map(ta -> ta.name).iterator(); } // other methods straightforward or throw exception } ``` Notice that the view presents a `Collection` interface, but it isn't a `HashSet` or `ArrayList`. It's a new way for callers to access the `minutesLeft` map in a restricted, safe way. All callers will share the same up-to-date adapter reference, but none will be able to modify the map or the objects it contains. What's a "stream"? [Something that's been around since Java 8](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)! It's an interface that supports functional operators like `map` and `filter`, among many others. According to the docs, it's: > A sequence of elements supporting sequential and parallel aggregate operations. You can do the above with anonymous classes, or with your own class that implements `Iterator<String>`, but I prefer this way. **Aside:** Note that I'll sometimes refer casually to a few different structural patterns as "proxies", but technically speaking they are different! In particular, we've just changed the interface: _adapter_ vs. _proxy_ might matter to your caller! ## Multiple Student Queues: Facade Pattern Suppose we have a variety of sources for student-signup data? How would we deal with not just one, but many `Iterator<Student>` objects being given to our `HoursDispatcher`? Well, we have a design decision to make. Let's fill in a skeleton, and what we know how to do: ```java= public class IteratorAggregator<T> implements Iterator<T> { List<Iterator<T>> queues = new ArrayList<>(); IteratorAggregator(Collection<Iterator<T>> startQueues) { queues.addAll(startQueues); } @Override public boolean hasNext() { // "Lambda can be replaced with method reference): // return queues.stream().anyMatch(Iterator::hasNext); return queues.stream().anyMatch(it -> it.hasNext()); } @Override public T next() { // We have a design decision to make... return null; } } ``` What's the decision? <details> <summary>Think, then click!</summary> How should the contents of the iterators be woven together? Should we loop through the iterators round-robin, and make new arrivals wait until that iterator comes around again? Or should we have a priority system where we'll always take values from earlier iterators, even if the later iterators never get to go? Should the contents of the queue be shuffled? What does it mean to shuffle a queue when new students may be arriving constantly? But, given a decision, we could implement a specific approach **and document it**. We could also ask for a strategy (and document that, too). </details> ## How To Approach All The Documents There are a lot of words related to Sprint 1. What are some of the documents that come to mind? At the very least, you should be thinking of the three component-specific documents: one for the REPL and CSV parser, one for the kd-tree, and one for the Bloom filter. If you're in charge of the REPL, do you think you need to read the kd-tree or Bloom filter documents? <details> <summary>Think, then click!</summary> Well, _kind of_. You need to skim the other documents enough to understand very broadly what your partner needs to do. But you don't need, or shouldn't need, to read it in detail. For example, being aware of what your partner's user stories are is _very helpful_, because it will increase the chances you agree on the right interface. But being aware of implementation-level details, suggestions, etc. is usually not a good use of your time. </details> </br> Read documents _breadth first_. ## Code Review Exercise Consider whether any of today's patterns might be useful to make this code better. See if you can articulate _why_ a specific use of a particular pattern helps. This code review involves 2 separate files: `Course.java` and `Admin.java`. ```java= import java.util.List; public class Course { final Date startDate; final int numStudents; final List<Student> studentList; final List<Student> remoteList; public Course(Date start, int newNumStudents, List<Student> newList){ numStudents = newNumStudents; this.startDate = new Date(start.getDate()); studentList = List.copyOf(newList); remoteList = new ArrayList<>(); } public int getNumStudents(){ return numStudents; } public void setNumStudents(int n){ numStudents = n; } public Date getDate(){ return startDate; } public void setDate(Date d){ startDate = d; } public List<Student> getStudentList(){ return studentList; } public void setRemoteList(List<Student> l){ for(int i = 0; i < l.size(); i++){ if(studentList.get(i).hasCovid()){ remoteList.set(i, l.get(i)); } } } } ``` ```java= public class Admin { final List<Course> courses; public Admin(List<Course> c){ courses = c; } public void covidPlan(Course c){ List<Student> remote = c.getStudentList(); for(Student s : remote){ if(s.hasCovid()){ s.setCovidRecords(null); } } } } ``` The code-review form is [here](https://forms.gle/JBCSg7H1ni3VNTmv7). I'd also like to get your answers to a (very quick) form about how you spent your time in the first week of Sprint 1. There's also a space to list concepts you'd like to hear more about. That form is [here](https://forms.gle/cXVeqpWrQxWKJuJ28).