Skip to content

Commit

Permalink
iluwatar#88 Created Facet pattern example and wrote most of the README.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy-Cains-ANU committed Oct 27, 2022
1 parent 4108f86 commit 04ed1f2
Show file tree
Hide file tree
Showing 14 changed files with 711 additions and 0 deletions.
178 changes: 178 additions & 0 deletions facet/README.md
@@ -0,0 +1,178 @@
---
title: Facet
category: Structural
language: en
tags:
- Decoupling
---

## Also known as

Attenuation

## Intent

Provide a interface to a powerful object in a restricted way, either by restricting parameters
or only allowing calls to a subset of object's functions.

## Explanation

Real-world example

> Imagine a knight fighting a dragon. The knight can only attempt to attack the dragon by trying
> different attacks. Here, the facet stops the knight from directly changing the health of the
> dragon, as well as only allowing certain attacks to affect the dragon.
In plain words

> Using the facet pattern, a class represents strictly limited access to another class.
C2 Wiki says that the intent is

> Restrict an interface to obtain a smaller interface that provides less authority. Usually the
> smaller interface has only a subset of the methods, or allows only a subset of parameter values.
**Programmatic Example**

Taking our knight versus dragon example from above. Firstly we have the `Knight` class and the
`Attack` enum containing attacks that can belong to knights.

```java
@Slf4j
public class Knight {
private final String name;
private Attack attack;
private DragonFacet dragonFacet;

public Knight (String name, Attack attack, DragonFacet dragonFacet) {
this.name = name;
this.attack = attack;
this.dragonFacet = dragonFacet;
}

public void attackDragon() {
int oldHealth = dragonFacet.getHealth();
dragonFacet.receiveAttack(attack);
if(oldHealth == dragonFacet.getHealth()){
LOGGER.info("{}: Darn it! {} did nothing.", name, attack);
} else {
LOGGER.info("{}: Huzzah! {} hurt that dastardly dragon.", name, attack);
}
}
}

public enum Attack {
ARROW, WATER_PISTOL, SWORD, FLAME_THROWER
}
```

Next are the `Dragon` class and the `DragonFacet` class. These belong to the same package, which is
different to the package containing `Knight`. This means that when there is no access level modifier to a variable or method, such as for `setHealth()`, nothing outside the package will have access to them. So, if the facet doesn't allow it then there is no way to access them.

Note that, according to C2 Wiki,

> Facets should be implemented in such a way that if methods are added to the original interface,
> they are not added by default to the facet. The methods to be included in a facet should have to
> be explicitly indicated (although this could be by metadata or a naming convention).
In this case, the methods are marked with `f_`.

This is the simple `Dragon` class.

```java
public class Dragon {
private int health;

public Dragon(int health) {
this.health = health;
}

int f_getHealth() {
return health;
}

void setHealth(int health) {
this.health = health;
}

void f_receiveAttack(Attack attack) {
switch(attack) {
case ARROW:
health -= 10;
break;
case WATER_PISTOL:
health -= 15;
break;
default:
health -= 5;
}
}
}
```

Then we have the `DragonFacet` to add control to `Dragon`.

```java
@Slf4j
public class DragonFacet {
private Dragon dragon;

public DragonFacet(Dragon dragon) {
this.dragon = dragon;
}

public int getHealth() {
return dragon.f_getHealth();
}

public void receiveAttack(Attack attack) {
if(attack == Attack.WATER_PISTOL || attack == Attack.ARROW) {
dragon.f_receiveAttack(attack);
}
}
}
```

Note that `DragonFacet` only provides access to two of the three methods in `Dragon`
(the ones marked with `f_`). Also, `receiveAttack` makes a check for valid parameters.

And here is the dragon-fighting scenario.

```java
var facet = new DragonFacet(new Dragon(100));
var sirJim = new Knight("Sir Jim", Attack.WATER_PISTOL, facet);
sirJim.attackDragon();
var sirLuke = new Knight("Sir Luke", Attack.FLAME_THROWER, facet);
sirLuke.attackDragon();
```

Program output:

```
Sir Jim: Huzzah! WATER_PISTOL hurt that dastardly dragon.
Sir Luke: Darn it! FLAME_THROWER did nothing.
```

## Class diagram

![alt text](./etc/proxy.urm.png "Facet pattern class diagram")

## Applicability

Facet is applicable when the client should have restricted access to an object. It is
a special type of proxy, but no extra functionality may be provided; that is, the facet
should only provide a subset of the object's functionality to a client.

This is often a security pattern, used in order to satisfy the Principle of Least
Authority. For example, if an object should be read-only, then the client should
be provided with a read-only facet.


## Related patterns

* [Proxy](https://java-design-patterns.com/patterns/proxy/)

## Credits

* [Facet Pattern](http://wiki.c2.com/?FacetPattern)
* [Capability Theory Glossary](http://www.cap-lore.com/CapTheory/Glossary.html)
Binary file added facet/etc/proxy.urm.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions facet/etc/proxy.urm.puml
@@ -0,0 +1,32 @@
@startuml
package com.iluwatar.facet {
class App {
+ App()
+ main(args : String[]) {static}
}
class IvoryTower {
- LOGGER : Logger {static}
+ IvoryTower()
+ enter(wizard : Wizard)
}
class Wizard {
- name : String
+ Wizard(name : String)
+ toString() : String
}
interface WizardTower {
+ enter(Wizard) {abstract}
}
class WizardTowerProxy {
- LOGGER : Logger {static}
- NUM_WIZARDS_ALLOWED : int {static}
- numWizards : int
- tower : WizardTower
+ WizardTowerProxy(tower : WizardTower)
+ enter(wizard : Wizard)
}
}
WizardTowerProxy --> "-tower" WizardTower
IvoryTower ..|> WizardTower
WizardTowerProxy ..|> WizardTower
@enduml
67 changes: 67 additions & 0 deletions facet/pom.xml
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
The MIT License
Copyright © 2014-2022 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>proxy</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.facet.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
53 changes: 53 additions & 0 deletions facet/src/main/java/com/iluwatar/facet/App.java
@@ -0,0 +1,53 @@
/*
* This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
*
* The MIT License
* Copyright © 2014-2022 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.facet;

import com.iluwatar.facet.dragon.*;

/**
* A facet is a class functioning as an interface to something else which provides less functionality.
* It is a wrapper often used for security purposes, so that the Principle of Least Authority is
* observed. It is technically a special case of proxy.
*
* <p>The Facet design pattern allows you to provide an interface to other objects by creating a
* wrapper class as the facet. The wrapper class, which is the facet, must only provide access to
* certain functions and under certain parameters, but never add functionality.
*
* <p>In this example the facet ({@link DragonFacet}) controls access to the actual object (
* {@link Dragon}).
*/
public class App {

/**
* Program entry point.
*/
public static void main(String[] args) {
var facet = new DragonFacet(new Dragon(100));
var sirJim = new Knight("Sir Jim", Attack.WATER_PISTOL, facet);
sirJim.attackDragon();
var sirLuke = new Knight("Sir Luke", Attack.FLAME_THROWER, facet);
sirLuke.attackDragon();
}
}
5 changes: 5 additions & 0 deletions facet/src/main/java/com/iluwatar/facet/Attack.java
@@ -0,0 +1,5 @@
package com.iluwatar.facet;

public enum Attack {
ARROW, WATER_PISTOL, SWORD, FLAME_THROWER
}
27 changes: 27 additions & 0 deletions facet/src/main/java/com/iluwatar/facet/Knight.java
@@ -0,0 +1,27 @@
package com.iluwatar.facet;

import com.iluwatar.facet.dragon.DragonFacet;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Knight {
private final String name;
private Attack attack;
private DragonFacet dragonFacet;

public Knight (String name, Attack attack, DragonFacet dragonFacet) {
this.name = name;
this.attack = attack;
this.dragonFacet = dragonFacet;
}

public void attackDragon() {
int oldHealth = dragonFacet.getHealth();
dragonFacet.receiveAttack(attack);
if(oldHealth == dragonFacet.getHealth()){
LOGGER.info("{}: Darn it! {} did nothing.", name, attack);
} else {
LOGGER.info("{}: Huzzah! {} hurt that dastardly dragon.", name, attack);
}
}
}

0 comments on commit 04ed1f2

Please sign in to comment.