Structural design mode and Structural Design Mode
Structural design pattern overview
The structural design pattern is used to process the combination between classes or objects, that is, to describe how the classes and objects are organized to form a large structure, so as to implement new functions.
Implementation Mechanism:
The structure object mode uses the combination/aggregation mechanism to combine classes, including the Bridge mode, Composite mode, Decorator mode, and Facade mode), FlyWeight, Proxy ).
The structural class model uses an Inheritance Mechanism to combine classes, including the Adapter mode ).
(1) appearance (Facade) mode (Facade Mode)
Question:
In a software system, the customer program is often coupled with the internal subsystems of a complex system, resulting in the change of the customer program as the subsystem changes. So, how to simplify the interaction interface between the customer program and the subsystem? How can we decouple the dependency between the internal subsystems of a complex system and the customer program?
(1) appearance (Facade) Mode
Public class Class1 {
Public void method1 (){
....
}
}
Public class Class2 {
Public void method2 (){
....
}
}
Public class Class3 {
Public void method3 (){
....
}
}
Public class Class4 {
Public void method4 (){
....
}
}
Assume that the customer program uses Class1, Class2, and Class4 to complete a business function, and uses Class3 and Class1 to complete another business function.
Public class ClientNoFacade {
Public void methodA () {// complete the first service function
Class1 c1 = new Class1 ();
C1.method1 ();
Class2 c2 = new Class2 ();
C2.method2 ();
Class4 c4 = new Class4 ();
C4.method4 ();
}
Public void methodB () {// complete the second service function
Class3 c3 = new Class3 ();
C3.method3 ();
Class1 c1 = new Class1 ();
C1.method1 ();}
}
(2) Decorator Mode
Question: In software systems, we sometimes use inheritance to extend the object's functions. However, due to the static characteristics introduced by inheritance as a type, this expansion method lacks flexibility; as the number of child classes increases (the number of extended functions), the combination of various child classes (the combination of extended functions) will lead to the expansion of more child classes. How can we enable "Object Function Extension" to be dynamically implemented as needed? At the same time, avoid the subclass expansion problem caused by "more extended functions?
Scenario:
Starbucks stores are almost everywhere in the world. They provide a wide range of delicious coffee: Irish coffee, Blue Mountain Coffee, kadiano, Nestle. Each coffee has its own descriptive attributes and billing behavior. They also provide a variety of ingredients: milk, sugar, ice, soy milk. The price for adding different ingredients to different coffee is different.
How to design?
It is a waste to implement it in an inherited way.
We have another idea:
Now the user has ordered a cup of Irish double milk coffee. What we need to do is:
Create an Irish coffee object
Decorating it with milk
Use milk to decorate it
Call the cost () method and rely on the Commission to calculate the price of the ingredients
Code implementation:
// Coffee Interface
Package com. lovo. decoretor;
Public interface Coffee {
Public int cost ();
}
// Modifier class
Package com. lovo. decoretor;
Public abstract class Drcoretor implements Coffee {
@ Override
Public abstractint cost ();
}
// Ingredients
Package com. lovo. decoretor;
Public class BingDecoretor extends Drcoretor {
Private Coffee coffee;
Public BingDecoretor (Coffee coffee ){
This. coffee = coffee;
}
Public int cost (){
// TODO Auto-generated method stub
Return 2 + coffee. cost ();
}
}
// Ingredients
Package com. lovo. decoretor;
Public class NaiDecoretor extends Drcoretor {
Private Coffee coffee;
Public NaiDecoretor (Coffee coffee ){
This. coffee = coffee;
}
Public int cost (){
// TODO Auto-generated method stub
Return 1 + coffee. cost ();
}
}
// Blue Mountain coffee
Package com. lovo. decoretor;
Public class LanShanCoffee implements Coffee {
@ Override
Public int cost (){
// TODO Auto-generated method stub
Return 10;
}
}
// Test
Package com. lovo. decoretor;
Public class TestDecoretor {
Public static void main (String [] args ){
Coffee coffee = new LanShanCoffee ();
// System. out. println (coffee. cost ());
Coffee = new NaiDecoretor (coffee );
Coffee = new BingDecoretor (coffee );
System. out. println (coffee. cost ());
}
}
In the decoration mode, the roles are:
Abstract Component role: provides an abstract interface to standardize the objects that are prepared to receive additional responsibilities.
Concrete Component role: defines a class that will receive additional responsibilities.
Decorator: holds an instance of a Component object and defines an interface consistent with the abstract Component Interface.
Concrete Decorator: attaches "additional responsibilities to the component object.
(3) Static Proxy Mode
Question: In a software system, some objects sometimes fail to or do not want to directly access another object because they span the network or other obstacles, if direct access brings unnecessary complexity to the system, an intermediate layer can be added between the client program and the target object to allow the proxy object to replace the target object.
For example:
Package com. lovo. proxy;
Public interface Book {
Public void SellBook ();
}
// Proxy
Package com. lovo. proxy;
Public class BookProxy implements Book {
Private RealSubject r;
Public BookProxy (RealSubject r ){
Super ();
This. r = r;
// R = new RealSubject ();
}
@ Override
Public void SellBook (){
R. SellBook ();
}
}
Package com. lovo. proxy;
Public class RealSubject implements Book {
@ Override
Public void SellBook (){
System. out. println ("selling books ");
}
}
// Test
Package com. lovo. proxy;
Public class Test {
Public static void main (String [] args ){
Book B = new BookProxy (new RealSubject ());
B. SellBook ();
}
}
(4) Bridge Mode
Question:
In software systems, some types of software systems have two or more dimensional changes due to their own logic. How can we deal with such "multi-dimensional changes "? How can we use object-oriented technology to make this type easily change in multiple directions without introducing additional complexity?
For example:
Package com. lovo. Bridge1;
Public interface Draw {
Public void draw ();
}
Package com. lovo. Bridge1;
Public interface Shap {
Public void mydraw ();
}
Package com. lovo. Bridge1;
Public class HuanShiXian implements Draw {
@ Override
Public void draw (){
System. out. println ("draw a solid line ");
}
}
Package com. lovo. Bridge1;
Public class HuaXuXian implements Draw {
@ Override
Public void draw (){
System. out. println ("dotted line ");
}
}
Package com. lovo. Bridge1;
Public class JuXing implements Shap {
Private int width;
Private int height;
Private Draw draw;
Public JuXing (int width, int height, Draw draw ){
Super ();
This. width = width;
This. height = height;
This. draw = draw;
}
Public int getWidth (){
Return width;
}
Public void setWidth (int width ){
This. width = width;
}
Public int getHeight (){
Return height;
}
Public void setHeight (int height ){
This. height = height;
}
@ Override
Public void mydraw (){
System. out. println ("draw a rectangle: Length:" + this. width + ", height:" + this. height );
Draw. draw ();
}
}
Package com. lovo. Bridge1;
Public class Test {
Public static void main (String [] args ){
Draw draw = new HuanShiXian ();
Shap s = new JuXing (100, 50, draw );
S. mydraw ();
}
}
(5) Adapter mode (class, Object Adapter Mode)
Question:
The interface of a class is transformed into another interface expected by the client, so that the two classes that cannot work together due to interface mismatch can work together.
Suppose we want to Piling, there are two types: Square Pile and circular pile.
Public class SquarePeg {
Public void insert (String str ){
System. out. println ("SquarePeg insert ():" + str );
}
}
Public class RoundPeg {
Public void insertIntohole (String msg ){
System. out. println ("RoundPeg insertIntoHole ():" + msg );
}
}
Now there is an application that requires both square and circular piles. then we need to apply these two unrelated classes comprehensively. if we do not have the source code or do not want to modify the source code of RoundPeg, we will use the Adapter to implement this application.
Public class PegAdapter extends SquarePeg {
Private RoundPeg roundPeg;
Public PegAdapter (RoundPeg peg ){
This. roundPeg = peg;
}
Public void insert (String str ){
RoundPeg. insertIntoHole (str );
}
}
In the code above, RoundPeg belongs to Adaptee and is the adapter.
The PegAdapter is an Adapter that adapts Adaptee (RoundPeg) and Target (Target SquarePeg.
In fact, this is a comprehensive application of the composition and inheritance methods.
PegAdapter inherits SquarePeg. If we need to inherit from both sides, that is, inherit SquarePeg and inherit RoundPeg, because many inheritance are not allowed in Java, but we can implement two interfaces (implements)
Public interface IRoundPeg {
Public void insertIntoHole (String msg );
}
Public interface ISquarePeg {
Public void insert (String str );
}
New RoundPeg and SquarePeg
Public class SquarePeg implements ISquarePeg {
Public void insert (String str ){
System. out. println ("SquarePeg insert ():" + str );
}
}
Public class RoundPeg implements IRoundPeg {
Public void insertIntohole (String msg ){
System. out. println ("RoundPeg insertIntoHole ():" + msg );
}
}
New PegAdapter
Public class PegAdapter implements IRoundPeg, ISquarePeg {
Private RoundPeg roundPeg;
Private SquarePeg squarePeg;
// Constructor
Public PegAdapter (RoundPeg roundPeg, SquarePeg squarePeg ){
This. roundPeg = roundPeg;
This. squarePeg = squarePeg;
}
}