forked from iluwatar/java-design-patterns
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
iluwatar#88 Created Facet pattern example and wrote most of the README.
- Loading branch information
1 parent
4108f86
commit 04ed1f2
Showing
14 changed files
with
711 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.iluwatar.facet; | ||
|
||
public enum Attack { | ||
ARROW, WATER_PISTOL, SWORD, FLAME_THROWER | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
Oops, something went wrong.