PrefaceThis article will discuss 23 design patterns in combination with actual practice. Each design pattern involves
Introducing design patterns in this way aims to help you better understand design patterns and get closer to them by combining them with the actual Android project development you encounter every day. At the same time, in actual development and refactoring, you can think about the refactoring methods and design patterns that can be applied, which can not only ensure that you write more reusable and reliable code, but also provide the best practices and summary of how to use the two pillars of refactoring and design patterns for elegant programming. At the same time, introducing 23 design patterns at one time in this way is also out of the idea that if you want to use a pattern, you should first know such a pattern. The Gang of Four's "Design Patterns" is also a summary of experience, but if there is a giant supporting you up, why bother to build a ladder in the dark? Refactoring is not the focus of this chapter, because this is also a very large topic. Here we only discuss whether there are some areas in the actual project that can be improved using design patterns. IntroductionDesign pattern is a set of repeatedly used, well-known, catalogued, and summarized code design experiences. Design patterns are used to reuse code, make it easier for others to understand, and ensure code reliability. There is no doubt that design patterns are a win-win for oneself, others, and the system. Design patterns make code compilation truly engineering-oriented. Design patterns are the cornerstone of software engineering, just like the bricks and stones of a building. The reasonable use of design patterns in projects can perfectly solve many problems. Each pattern has a corresponding principle in the present. Each pattern describes a problem that keeps happening around us, as well as the core solution to the problem. This is why it can be widely used. Six principlesSingle Responsibility PrincipleThe single principle is simple, which is to encapsulate a group of highly related functions and data into one class. In other words, a class should have a single responsibility. Open/Closed PrincipleThe open-closed principle is not complicated to understand. It means that a class should be open to extension but closed to modification. When writing code, you should try to implement new functions through extension rather than by modifying existing code. Otherwise, it is easy to destroy the original system and may also bring new problems. If you find that it cannot be implemented through extension, you should consider whether it is a problem in the code structure and solve it through refactoring. Liskov Substitution PrincipleAll places that reference the base class must be able to transparently use its subclass objects. Essentially, this means making good use of inheritance and polymorphism, declaring variables (or parameters) in the form of the parent class, and assigning variables (or parameters) to any subclass that inherits from this parent class. Dependency Inversion PrincipleDependency inversion is mainly about achieving decoupling, so that high-level modules do not depend on the specific implementation details of low-level modules. To understand it, we need to know several key points:
In the Java language we use, abstraction refers to interfaces or abstract classes, both of which cannot be instantiated directly; details are implementation classes, and classes that implement interfaces or inherit abstract classes are details. Using Java language to describe it is: the parameters passed between modules are declared as abstract types, rather than as specific implementation classes; Interface Segregation PrincipleDependencies between classes should be based on the smallest interface. The principle is to split very large and bloated interfaces into smaller and more specific interfaces. Demeter PrincipleAn object should have minimal knowledge of other objects. Suppose class A implements a certain function, and class B needs to call class A to execute this function, then class A should only expose one function to class B, which represents the function that implements this function, rather than letting class A expose all the subdivided functions that implement this function to B. Design PatternsSingleton Patterndefinition Make sure that the singleton class has only one instance, and the singleton class provides a function interface for other classes to obtain this unique instance. Practical Application
// Singleton object
private static AdvertPresenter mInstance;
/** * Private constructor */
private AdvertPresenter () {
}
/** * Get the AdvertPresenter instance * @return */
public static AdvertPresenter getInstance () {
if (mInstance == null ) {
synchronized (AdvertPresenter.class) {
if (mInstance == null ) {
mInstance = new AdvertPresenter();
}
}
}
return mInstance;
} Android //Get the WindowManager service reference
WindowManager wm = ( WindowManager )getSystemService(getApplication(). WINDOW_SERVICE ); Internally, it holds a WindowManager in a singleton manner and returns this object RefactoringThere are multiple operations using Random and Gson in the project. You can encapsulate the Random and Gson objects into singletons for use. Builder Patterndefinition Separating the construction of a complex object from its representation allows the same construction process to create different representations. ExampleVarious custom Dialogs Android
AlertDialog.Builer builder= new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon)
.setTitle( "title" )
.setMessage( "message" )
.setPositiveButton( "Button1" ,
new DialogInterface.OnclickListener(){
public void onClick (DialogInterface dialog, int whichButton) {
setTitle( "click" );
}
})
.create()
.show(); RefactoringNone Prototype Patterndefinition Use prototype instances to specify the type of object to be created, and create new objects by copying these prototypes. Example
private HashMap getClonePointMap(Map map) {
HashMap clone = new HashMap<>();
if (map != null ) {
Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String) entry.getKey();
PointBean pointBean = (PointBean) entry.getValue();
if (pointBean != null ) {
//Traverse the map and put the cloned objects into the new map
clone .put(key, pointBean. clone ());
} else {
clone .put(key, null );
}
}
}
return clone ;
} Android
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
//Clone the copy
Intent copyIntent=(Intetn) shareIntent.clone (); RefactoringIf you want to remove the parameter values of an object one by one and assign them to another object, you can use the prototype mode. Factory PatternSimple factory patterndefinitionCreate a factory (a function or a class method) to make new objects. Example public static Operation createOperate ( string operate ) {
Operation oper = null ;
switch (operate)
{
case "+" :
{
oper = new OperationAdd();
break ;
}
case "-" :
{
oper = new OperationSub();
break ;
}
case "*" :
{
oper = new OperationMul();
break ;
}
case "/" :
{
oper = new OperationDiv();
break ;
}
}
return oper;
}
} Android public Object getSystemService (String name) {
if (getBaseContext() == null ) {
throw new IllegalStateException( "System services not available to Activities before onCreate()" );
}
//........
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
//.......
return super .getSystemService(name);
} The simple factory pattern is used in the getSystemService method. The decision on which object to create is based on the passed in parameters. Since these objects have been created in advance in singleton mode, there is no need to create a new one here. Just return the singleton directly. Refactoring //Before reconstruction
public class AdvertPresenter {
...
private void initAdvertManager () {
String[] platforms = mAdConfig.getAllPlatforms();
if (platforms != null && platforms.length > 0 ) {
int platformSize = platforms.length;
for ( int i = 0 ; i < platformSize; i++) {
String platform = platforms[i];
if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_FACEBOOK)) {
FacebookAdvertManager fbAdManager = new FacebookAdvertManager();
mAdvertManager.put(AdvertConstant.AD_PLATFORM_FACEBOOK, fbAdManager);
} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADMOB)) {
AdMobAdvertManager adMobAdvertManager = new AdMobAdvertManager();
mAdvertManager.put(AdvertConstant.AD_PLATFORM_ADMOB, adMobAdvertManager);
} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_MOPUB)) {
MopubAdvertManager mopubAdvertManager = new MopubAdvertManager();
mAdvertManager.put(AdvertConstant.AD_PLATFORM_MOPUB, mopubAdvertManager);
} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADX)) {
AdxAdvertManager mopubAdvertManager = new AdxAdvertManager();
mAdvertManager.put(AdvertConstant.AD_PLATFORM_ADX, mopubAdvertManager);
}
}
}
}
...
}
//After reconstruction
public class BaseAdvertManager {
...
public static BaseAdvertManager create (String platform) {
if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_FACEBOOK)) {
return new FacebookAdvertManager();
} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_MOPUB)) {
return new MopubAdvertManager();
} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADX)) {
return new AdxAdvertManager();
} else if (platform.equalsIgnoreCase(AdvertConstant.AD_PLATFORM_ADMOB)) {
return new AdMobAdvertManager();
} else {
*** return new NullAdvertManager();*** //Introduce NULL object
}
}
...
}
public class AdvertPresenter {
...
private void initAdvertManager () {
String[] platforms = mAdConfig.getAllPlatforms();
if (platforms != null && platforms.length > 0 ) {
int platformSize = platforms.length;
for ( int i = 0 ; i < platformSize; i++) {
String platform = platforms[i];
mAdvertManager.put(platform, BaseAdvertManager.create(platform));
}
}
}
...
} Factory Method PatterndefinitionIt is to define a factory interface for creating product objects, let its subclasses decide which class to instantiate, and defer the actual creation work to the subclasses. Example public abstract class Product {
public abstract void method () ;
}
public class ConcreteProduct extends Prodect {
public void method () {
System.out.println( "I am a specific product!" );
}
}
public abstract class Factory {
public abstract Product createProduct () ;
}
public class ConcreteFactory extends Factory {
public Product createProduct () {
return new ConcreteProductA();
}
} AndroidWe will use a lot of data structures in development, such as ArrayList, HashMap, etc. Let's first look at the brief UML diagram of the collection framework of the Collection part in Java. public interface Iterable {
/** * Returns an iterator over elements of type { @code T}. * * @return an Iterator. */
Iterator iterator () ;
//Omit some code
} List and Set inherit from Collection interface, which inherits from Iterable interface. So List and Set interface also need to inherit and implement the iterator() method in Iterable. Then the iterator method in two commonly used indirect implementation classes ArrayList and HashSet specifically constructs and returns an iterator object for us. @Override
public Iterator iterator () {
return new ArrayListIterator();
} The ArrayListIterator type is defined as follows: private class ArrayListIterator implements Iterator {
/** Number of elements remaining in this iteration */
private int remaining = size;
/** Index of element that remove() would remove, or -1 if no such elt */
private int removalIndex = - 1 ;
/** The expected modCount value */
private int expectedModCount = modCount;
public boolean hasNext () {
return remaining != 0 ;
}
@SuppressWarnings ( "unchecked" ) public E next () {
ArrayList ourList = ArrayList.this ;
int rem = remaining;
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (rem == 0 ) {
throw new NoSuchElementException();
}
remaining = rem - 1 ;
return (E) ourList.array[removalIndex = ourList.size - rem];
}
public void remove () {
Object[] a = array;
int removalIdx = removalIndex;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (removalIdx < 0 ) {
throw new IllegalStateException();
}
System.arraycopy(a, removalIdx + 1 , a, removalIdx, remaining);
a[--size] = null ; // Prevent memory leak
removalIndex = - 1 ;
expectedModCount = ++modCount;
}
} We see that this class implements the Iterator interface, which is defined as follows: public interface Iterator {
boolean hasNext () ;
E next () ;
default void remove () {
throw new UnsupportedOperationException( "remove" );
}
default void forEachRemaining (Consumer action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
} Now that the basic structure has been analyzed, let's take a look at how to implement the factory method pattern. RefactoringNone Abstract Factory PatterndefinitionProvides an interface for creating a group of related or interdependent objects without specifying their concrete classes. The abstract factory pattern is a factory pattern used when there are multiple abstract roles. The abstract factory pattern can provide an interface to the client, allowing the client to create product objects in multiple product families without having to specify the specific products. Example public abstract class AbstractProductA {
public abstract void method () ;
}
public abstract class AbstractProdectB {
public abstract void method () ;
}
public class ConcreteProductA1 extends AbstractProductA {
public void method () {
System.out.println( "Specific method of product A1!" );
}
}
public class ConcreteProductA2 extends AbstractProductA {
public void method () {
System.out.println( "Specific method of product A2!" );
}
}
public class ConcreteProductB1 extends AbstractProductB {
public void method () {
System.out.println( "Specific method of product B1!" );
}
}
public class ConcreteProductB2 extends AbstractProductB {
public void method () {
System.out.println( "Specific method of product B2!" );
}
}
public abstract class AbstractFactory {
public abstractAbstractProductA createProductA () ;
public abstractAbstractProductB createProductB () ;
}
public class ConcreteFactory1 extends AbstractFactory {
public AbstractProductA createProductA () {
return new ConcreteProductA1();
}
public AbstractProductB createProductB () {
return new ConcreteProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory {
public AbstractProductA createProductA () {
return new ConcreteProductA2();
}
public AbstractProductB createProductB () {
return new ConcreteProductB2();
}
} AndroidDue to the limitations of this mode, it is rarely used in Android. IPolicy in the com.android.internal.policy package uses this mode. It is an abstract factory for a series of window-related products such as Android windows, window management, layout loading, and event fallback handlers. However, there is only one specific factory implementation in the source code. Because this part has a relatively complex structure and a large amount of code, interested students can check the relevant information or read the source code by themselves. Contrast with the Factory Method Patternuse
the difference
advantage
shortcoming
Because the abstract factory is not easy to expand new product families, this design pattern is rarely used when providing access to external personnel. Some people also say that the abstract factory method pattern is a very "disgusting" design pattern. The most typical application is the original purpose of this pattern, that is, to build a view family to adapt to the views under the two operating systems of Unit and Windows, and the view family has different implementations; the other is the object operation family formed by the operation of different databases in the operation of Java connecting to the database, but when the data is changed again, the modification of the interface required is also very troublesome, so the scalability is not good. RefactoringNone Strategy PatterndefinitionThere is a series of algorithms, each algorithm is encapsulated (each algorithm can be encapsulated into a different class), and each algorithm can be replaced. The strategy pattern allows the algorithm to change independently of the clients who use it. Example public abstract class BaseAdvertManager {
protected abstract void doLoadAdvert () ;
}
public class FacebookAdvertManager extends BaseAdvertManager {
@Override
protected void doLoadAdvert () {
Log.v(TAG, "Loading Facebook ads" );
}
}
public class AdmobAdvertManager extends BaseAdvertManager {
@Override
protected void doLoadAdvert () {
Log.v(TAG, "Load Admob ads" );
}
} AndroidAndroid uses the strategy pattern when using time interpolators in property animation. When using animation, you can choose LinearInterpolator, AccelerateDecelerateInterpolator, DecelerateInterpolator, and custom interpolators. These interpolators all calculate the percentage of change in the current property value based on the percentage of time elapsed. By selecting different interpolators as needed, different animation effects can be achieved. RefactoringNone State PatterndefinitionIn the state pattern, the behavior is determined by the state, and different states have different behaviors. The structure of the state pattern and the strategy pattern are almost the same, but the purpose and nature of their expression are different. Example public interface TvState {
public void nextChannerl () ;
public void prevChannerl () ;
public void turnUp () ;
public void turnDown () ;
}
public class PowerOffState implements TvState {
public void nextChannel () {}
public void prevChannel () {}
public void turnUp () {}
public void turnDown () {}
}
public class PowerOnState implements TvState {
public void nextChannel () {
System.out.println( "Next channel" );
}
public void prevChannel () {
System.out.println( "Previous channel" );
}
public void turnUp () {
System.out.println( "Turn up the volume" );
}
public void turnDown () {
System.out.println( "Turn down the volume" );
}
}
public interface PowerController {
public void powerOn () ;
public void powerOff () ;
}
public class TvController implements PowerController {
TvState mTvState;
public void setTvState (TvStete tvState) {
mTvState=tvState;
}
public void powerOn () {
setTvState( new PowerOnState());
System.out.println( "Starting up" );
}
public void powerOff () {
setTvState( new PowerOffState());
System.out.println( "Shutdown" );
}
public void nextChannel () {
mTvState.nextChannel();
}
public void prevChannel () {
mTvState.prevChannel();
}
public void turnUp () {
mTvState.turnUp();
}
public void turnDown () {
mTvState.turnDown();
}
}
public class Client {
public static void main (String[] args) {
TvController tvController= new TvController();
tvController.powerOn();
tvController.nextChannel();
tvController.turnUp();
tvController.powerOff();
//Turn up the volume, it will not take effect at this time
tvController.turnUp();
}
} AndroidThe state mode is used in many places in the Android source code. For example, the Android WIFI management module. When WIFI is turned on, it automatically scans the surrounding access points and displays them in a list; when WIFI is turned off, it clears them. Here, the WIFI management module performs different actions according to different states. Difference from Strategy PatternThe behavior of the state pattern is parallel and irreplaceable, while the strategy pattern is an object behavior pattern whose behaviors are independent and replaceable. RefactoringThere are switches for functions such as face slimming in the project. Now they are judged by configuration files, and can be reconstructed through state mode. Then, when processing pictures, the polymorphic characteristics can be used to directly use objects for processing. Chain of Responsibility PatterndefinitionGive multiple objects the opportunity to process the request, thereby avoiding direct coupling between the sender and receiver of the request, connecting these objects into a chain, and passing the request along the chain until an object processes it. Example /** * Abstract handler */
public abstract class Handler {
/** * Holds the successor responsible object */
protected Handler successor;
/** * This is a method for processing requests. Although this method does not have any parameters passed in, * it is actually possible to pass parameters in. You can choose whether to pass parameters based on your specific needs. */
public abstract void handleRequest () ;
/** * Value retrieval method */
public Handler getSuccessor () {
return successor;
}
/** * Assignment method, set the subsequent responsible object */
public void setSuccessor ( Handler successor ) {
this .successor = successor;
}
}
/** * Specific handler */
public class ConcreteHandler extends Handler {
/** * Processing method, call this method to process the request */
@ Override public void handleRequest () {
/** * Determine whether there is a successor responsible object * If yes, forward the request to the successor responsible object * If not, process the request */
if (getSuccessor() != null )
{
System.out.println( "Return request" ) ;
getSuccessor().handleRequest();
} else
{
System.out.println( "Processing request" ) ;
}
}
}
/** * The client class that initiates the request */
public class Client {
public static void main ( String[] args ) {
//Assembly responsibility chain
Handler handler1 = new ConcreteHandler();
Handler handler2 = new ConcreteHandler();
handler1.setSuccessor(handler2);
//Submit the request
handler1.handleRequest();
}
} AndroidWhen Android processes a click event, the parent View receives the click event first. If the parent View does not process it, it is handed over to the child View, passing the responsibility down in sequence. Java's exception capture mechanism is also a manifestation of the chain of responsibility model. RefactoringNone Interpreter modedefinitionGiven a language, define its grammar, and define an interpreter that is used to parse the language. ExampleFor example, you can write configuration files for various functional modules, and then load the configuration files as configuration objects during operation according to the configuration file writing rules defined by the project. This mode should be used more or less in daily projects, so I will not post the code. AndroidThis is used in many places. One of them is that the four major components of Android need to be defined in AndroidManifest.xml. In fact, AndroidManifest.xml defines the attributes of tags (statements) and their sub-tags, stipulates the specific use (grammar), and is parsed by PackageManagerService (interpreter). RefactoringNone Command Modedefinition The command pattern encapsulates each request into an object, allowing users to parameterize the client with different requests; queue or log requests, and support undoable operations. Example
public void method () {
Handler.post( new Runnable() {
@Override
public void run () {
clearCache();
statics();
finish();
}
});
} AndroidIn the Android event mechanism, the underlying logic forwards and processes events. Each key event will be encapsulated into a NotifyKeyArgs object, and the specific event operation will be encapsulated through the InputDispatcher. Another example is the Runnable we use. We can use it to encapsulate the operations we want to do, and then hand them over to the Handler to process in sequence, or remove them before processing. RefactoringThe current advertising module business logic can be reconstructed in this way. Requests for ad pulling and ad display can be encapsulated into objects, placed in a management container and called according to certain rules. This can not only avoid the situation where ads are being pulled when the ad display interface is called, resulting in loss of display times, but also cancel some steps when abnormal situations such as network errors occur. Observer PatterndefinitionSometimes called the publish/subscribe pattern, it defines a one-to-many dependency relationship, allowing multiple observer objects to listen to a subject object at the same time. When the subject object changes its state, it notifies all observer objects so that they can automatically update themselves. Example Java's Observable class and Observer interface implement the observer pattern. An Observer object monitors the changes of an Observable object. When the Observable object changes, the Observer is notified and can perform corresponding work. AndroidThe ListView adapter has a notifyDataSetChange() function, which notifies each Item in the ListView that the data source has changed and each sub-Item needs to be refreshed. RefactoringFor example, after selecting a filter and taking a photo, the camera enters the selfie confirmation page. If the user changes the filter, the filter on the camera interface should be replaced with the latest selected filter when returning. Currently, this logic is implemented in the onResume function of the camera interface, but in fact, the filter can be extracted into a complete module, and then the observer mode can be implemented in the module. This can encapsulate the filter logic and decouple it from the outside world, which is also in line with the concept of Java multi-purpose combination. EventBusThe benefits of EventBus are obvious. It can easily and simply implement the observer mode, but the disadvantages are also obvious.
Therefore, if there is a need to use the observer mode, it is recommended to implement the observer mode in this module if all conditions permit. If you find that this function is also needed in other modules, then you should consider whether this series of functions should be extracted into a more independent module for reuse, rather than using EventBus directly for the sake of convenience. Memento PatterndefinitionWithout destroying the closure, capture the internal state of an object and save the state outside the object, so that the object can be restored to the previously saved state later. ExampleSerialize objects to local storage and deserialize them back when necessary
public PhotoData readPhotoData (String path) {
PhotoData photoData = null ;
ObjectInputStream objInput = null ;
try {
objInput = new ObjectInputStream( new FileInputStream(path));
photoData = (PhotoData) objInput.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
finally
try {
if (objInput != null ) {
objInput.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return photoData;
}
public void writePhotoData (PhotoData data, String path) {
ObjectOutputStream objOutput = null ;
try {
objOutput = new ObjectOutputStream( new FileOutputStream(path));
objOutput.writeObject(data);
} catch (IOException e) {
e.printStackTrace();
finally
try {
if (objOutput != null ) {
objOutput.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} AndroidActivity's onSaveInstanceState and onRestoreInstanceState use the memo mode, which are used for saving and restoring respectively. RefactoringNone Iterator Patterndefinition Provides a way to sequentially access the elements of a container object without exposing the object's internal representation. ExampleJava's Iterator is an abstract iterator. By implementing this interface, each collection can provide its own customized specific iterator. If you are interested, you can directly view the source code. Although we use collections in many scenarios, the actual use of iterators is relatively rare. For simple traversal (such as arrays or ordered lists), it is more cumbersome to use iterators to traverse. For example, ArrayList, we prefer to use for loops to traverse, but for collections such as hash tables, it is much simpler to introduce iterators. At the same time, we can also use custom iterators to provide forward or reverse traversal of ordered lists. Users only need to get iterators to traverse without worrying about the specific traversal algorithm. AndroidIn the Android source code, the most typical example is that Cursor uses the iterator mode. When we use the query method of SQLiteDatabase, the Cursor object is returned, and then the Cursor is used to traverse the data. RefactoringNone Template Method PatterndefinitionDefine the algorithm framework in an operation and defer some steps to subclasses so that subclasses can redefine certain specific steps of an algorithm without changing its structure. Example public abstract class BaseAdvertManager {
public void loadAdvert (Context context, int type) {
if (NetUtils.checkNetConnection(context) != NetUtils.OK) {
return ;
}
mStateArray.put(type, AdvertConstant.AD_STATE_LOADING);
doLoadAdvert(context, type);
}
protected abstract void doLoadAdvert (Context context, int type) ;
} The Ad Manager abstract class defines a general template for loading ads, but declares the specific logic of loading ads in the template as an abstract method for each subclass to implement. AndroidThe process of starting an Activity is very complicated, and there are many places that need to be customized by developers. That is to say, the overall algorithm framework is the same, but some steps are delayed to the subclass, such as Activity's onCreate, onStart, etc. In this way, the subclass can redefine some specific operations without changing the overall process of starting the Activity. RefactoringYou can try to use the template method to refactor methods that have most of the same code but different parts of the code. You can either refactor the method in the same class or refactor the subclass and parent class by shaping the template function. In the project, saving photos and sharing and saving photos and backing out on the selfie confirmation page are currently two independent methods, but most of the code inside the methods is the same, so you need to use this pattern to refactor. Since the code is large and the refactoring method is relatively simple, I will not post the code. Visitor Patterndefinition Encapsulates some operations that act on the elements of a data structure. It can define new operations that act on these elements without changing the data structure. In most cases, you need to use visitor mode, but when you need it, you really need it. ——GOF Design Pattern: The Basics of Reusable Object-Oriented Software Example interface Service {
public void accept (Visitor visitor) ;
}
class Draw implements Service {
public void accept (Visitor visitor) {
visitor.process( this );
}
}
class Fund implements Service {
public void accept (Visitor visitor) {
visitor.process( this );
}
}
class Saving implements Service {
public void accept (Visitor visitor) {
visitor.process( this );
}
}
class Visitor {
public void process (Service service) {
// Basic business
System.out.println( "Basic Business" );
}
public void process (Saving service) {
// deposit
System.out.println( "Deposit" );
}
public void process (Draw service) {
// Withdrawal
System.out.println( "withdrawal" );
}
public void process (Fund service) {
System.out.println( "Fund" );
// fund
}
}
public class Client {
public static void main (String[] args) {
Service saving = new Saving();
Service fund = new Fund();
Service draw = new Draw();
Visitor visitor = new Visitor();
saving.accept(visitor);
fund.accept(visitor);
draw.accept(visitor);
}
} The benefits of using Visitor are shown above. When you need to change the processing of one of the services, you do not need to modify it in every place, but just need to change the corresponding processing functions in the Visitor class. That is to say, it is suitable for situations where business processing changes frequently. AndroidThe use of visitor mode in Android is actually mainly in the compilation annotation. The core principles of compilation annotation rely on APT (Annotation Processing Tools). Famous open source libraries such as ButterKnife, Dagger, and Retrofit are all based on APT. Refactoring If it is a business that often requires changes in logic, it is very suitable for using the visitor mode. If it is a business that requires frequent addition of new services, it is not suitable. Therefore, the Android UI display part is actually theoretically suitable for using the visitor mode, because the UI often changes one version at a time. If the changes in the UI are not limited to modifications in XML, but have been reflected in the code, then it can be considered whether it can be modified using the visitor mode. Intermediary modeldefinition The mediator pattern wraps a series of ways in which objects interact, so that these objects do not have to be called clearly from each other, so that they can be easily coupled. When the effects between some objects change, the effects between some other objects will not immediately affect the effects of some other objects. The mediator pattern turns many-to-many interactions into one-to-many interactions. Example public abstract class Person {
protected String name;
protected Mediator mediator;
Person(String name,Mediator mediator){
this .name = name;
this .mediator = mediator;
}
}
public class HouseOwner extends Person {
HouseOwner(String name, Mediator mediator) {
super (name, mediator);
}
/** * @desc Contact the intermediary* @param message * @return void */
public void constact (String message) {
mediator.constact(message, this );
}
/** * @desc Get information* @param message * @return void */
public void getMessage (String message) {
System.out.println( "Homeowner:" + name + ",get information: " + message);
}
}
public class Tenant extends Person {
Tenant(String name, Mediator mediator) {
super (name, mediator);
}
/** * @desc Contact the intermediary* @param message * @return void */
public void constact (String message) {
mediator.constact(message, this );
}
/** * @desc Get information* @param message * @return void */
public void getMessage (String message) {
System.out.println( "Renter:" + name + ",get information: " + message);
}
}
public abstract class Mediator {
//Declare a contact method
public abstract void constact (String message,Person person) ;
}
public class MediatorStructure extends Mediator {
//First of all the intermediary structure must know the information of all homeowners and renters
private HouseOwner houseOwner;
private Tenant tenant;
public HouseOwner getHouseOwner () {
return houseOwner;
}
public void setHouseOwner (HouseOwner houseOwner) {
this .houseOwner = houseOwner;
}
public Tenant getTenant () {
return tenant;
}
public void setTenant (Tenant tenant) {
this .tenant = tenant;
}
public void constact (String message, Person person) {
if (person == houseOwner){ //If it is a homeowner, the renter will get information
tenant.getMessage(message);
}
else { //Anyway, it is the homeowner who gets the information
houseOwner.getMessage(message);
}
}
}
public class Client {
public static void main (String[] args) {
//A homeowner, a renter, an intermediary agency
MediatorStructure mediator = new MediatorStructure();
//Homeowners and renters only need to know the agency
HouseOwner houseOwner = new HouseOwner( "张三" , mediator);
Tenant tenant = new Tenant( "李四" , mediator);
//Agent structure requires knowing the homeowner and renter
mediator.setHouseOwner(houseOwner);
mediator.setTenant(tenant);
tenant.constact( "I heard that you have a three-bedroom homeowner for rent..." );
houseOwner.constact( "Yes! Do you need to rent?" );
}
}
AndroidIn the Binder mechanism, the intermediary mode is used. We know that when the system is started, various system services will submit registration to the ServiceManager, that is, the ServiceManager holds references to various system services. When we need to obtain the system's ServiceManager, such as ActivityManager, WindowManager, etc. (they are all Binders), first, we query the ServiceManager for the corresponding Binder of the specified identifier, and then the ServiceManager returns the Binder reference. And the communication between the client and the server is implemented through the Binder driver, where the ServiceManager and Binder drivers are intermediaries. RefactoringSince the beginning of the year, there has been a refactoring of MVP in the project. In the MVP architecture , the P layer is actually an intermediary, responsible for coordinating V and M. Appearance mode/facade modedefinition It is required that the communication between the external and internal of a subsystem must be carried out through a unified object. Example /** * cpu subsystem class*/
public class CPU {
public static final Logger LOGGER = Logger.getLogger(CPU.class);
public void start () {
LOGGER.info( "cpu is start..." );
}
public void shutDown () {
LOGGER.info( "CPU is shutDown..." );
}
}
/** * Disk subsystem class*/
public class Disk {
public static final Logger LOGGER = Logger.getLogger(Disk.class);
public void start () {
LOGGER.info( "Disk is start..." );
}
public void shutDown () {
LOGGER.info( "Disk is shutDown..." );
}
}
/** * Memory subsystem class*/
public class Memory {
public static final Logger LOGGER = Logger.getLogger(Memory.class);
public void start () {
LOGGER.info( "Memory is start..." );
}
public void shutDown () {
LOGGER.info( "Memory is shutDown..." );
}
}
/** * Facade Class (Core) */
public class Computer {
public static final Logger LOGGER = Logger.getLogger(Computer.class);
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer () {
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void start () {
LOGGER.info( "Computer start begin" );
cpu.start();
disk.start();
memory.start();
LOGGER.info( "Computer start end" );
}
public void shutDown () {
LOGGER.info( "Computer shutDown begin" );
cpu.shutDown();
disk.shutDown();
memory.shutDown();
LOGGER.info( "Computer shutDown end..." );
}
}
/** * Client class*/
public class Cilent {
public static final Logger LOGGER = Logger.getLogger(Cilent.class);
public static void main (String[] args) {
Computer computer = new Computer();
computer.start();
LOGGER.info( "=================" );
computer.shutDown();
}
} Judging from the above example, with this Facade class, that is, the Computer class, the user does not have to call the Disk, Memory, and CPU classes in the subsystem themselves. He does not need to know the implementation details within the system, nor does he even need to know the internal composition of the system. The client only needs to interact with Facade. AndroidSo where does Android use the appearance mode? Still back to Context, there are many complex functions inside Android such as startActivty, sendBroadcast, bindService, etc. The internal implementation of these functions is very complex. If you read the source code, you can feel it, but we don’t need to care about what is implemented inside it. We only care about it helping us start the Activity, send a broadcast for us, bind the Activity, etc. RefactoringNone Proxy Modedefinition Provides a proxy to an object, and the proxy object controls the reference to the original object. Static ProxydefinitionStatic proxy is relatively simple. It is a proxy class written by programmers and compiled before the program runs, rather than dynamically generating proxy classes from the program. This is called static. It can be implemented through two ways: aggregation and inheritance. The inheritance method is not flexible enough, so only the aggregation method is introduced. Example
nterface Subject {
void request () ;
}
class RealSubject implements Subject {
public void request () {
System.out.println( "RealSubject" );
}
}
class Proxy implements Subject {
private Subject subject;
public Proxy (Subject subject) {
this .subject = subject;
}
public void request () {
System.out.println( "begin" );
subject.request();
System.out.println( "end" );
}
}
public class ProxyTest {
public static void main (String args[]) {
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
} RefactoringCurrently, there are business needs in the project to load images. The frames for loading images can include ImageLoader, Glide , etc., and these third parties or their own internal tool classes can be integrated together through the adapter mode, and then provided to external use of image processing through static agents. Dynamic ProxydefinitionIn dynamic proxy, the proxy class is not implemented in Java code, but is generated during the runtime. Compared with static proxy, dynamic proxy can conveniently handle the methods of the delegate class in a unified manner, such as adding method calls, adding log functions, etc. Dynamic proxy is divided into jdk dynamic proxy and cglib dynamic proxy. Let’s use an example to see how to implement jdk dynamic proxy. JDK dynamic proxy example //Define business logic
public interface Service {
//Target method
public abstract void add () ;
}
public class UserServiceImpl implements Service {
public void add () {
System.out.println( "This is add service" );
}
}
//Use the java.lang.reflect.Proxy class and java.lang.reflect.InvocationHandler interface to define the implementation of the proxy class.
class MyInvocatioHandler implements InvocationHandler {
private Object target;
public MyInvocatioHandler (Object target) {
this .target = target;
}
@Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
System.out.println( "-----before-----" );
Object result = method.invoke(target, args);
System.out.println( "-----end-----" );
return result;
}
// Generate proxy object
public Object getProxy () {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this );
}
}
//Use dynamic proxy
public class ProxyTest {
public static void main (String[] args) {
Service service = new UserServiceImpl();
MyInvocatioHandler handler = new MyInvocatioHandler(service);
Service serviceProxy = (Service)handler.getProxy();
serviceProxy.add();
}
}
Execution Result:
-----before-----
This is add service
-----end----- cglib dynamic proxy example As we analyzed earlier, because Java only allows single inheritance, and the proxy class generated by JDK itself inherits the Proxy class, the dynamic proxy implemented using JDK cannot complete the inherited dynamic proxy, but we can use cglib to implement the inherited dynamic proxy. //1. Specific topic
public class Train {
public void move () {
System. out .println( "The train is moving..." );
}
}
//2. Generate a proxy
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy ( Class clazz ) {
enhancer.setSuperclass(clazz);
enhancer.setCallback( this );
return enhancer.create();
}
/** * Intercept all target class methods calls* Parameters: * obj target instance object* method reflect object* args method parameters* proxy proxy class instance*/
public Object intercept ( Object obj, Method method, Object[] args, MethodProxy proxy ) throws Throwable {
//The proxy class calls the parent class method
System. out .println( "Login Start" );
proxy.invokeSuper(obj, args);
System. out .println( "Login End" );
return null ;
}
}
//3. Test
public class Test {
public static void main ( String[] args ) {
CGLibProxy proxy = new CGLibProxy();
Train t = (Train) proxy.getProxy(Train.class);
t.move();
}
} RefactoringIn many places in the project, statistics are needed. It feels inappropriate to put this function on any layer. Simple statistics can be extracted through dynamic proxy. Dynamic proxy is actually a concrete implementation of AOP programming idea summaryCompared with static proxy, the biggest advantage is that all methods declared in the interface are transferred to a centralized method to call the processor for processing. When the number of interface methods is large, we can handle it flexibly without the need to process each method or method combination like a static proxy. Proxy is beautiful and powerful, but only supports interface proxy. Java's single inheritance mechanism destined that these dynamic proxy classes cannot implement dynamic proxy for class. Fortunately, cglib provides compensation for Proxy. The difference between class and interface is vague, and some new features have been added in java8, making interface closer and closer to class. When one day, Java breaks through the limitation of single inheritance, dynamic proxy will be more powerful. Android AIDL will judge whether to access cross-process based on the current thread. If you do not need to cross-process, you will return the instance directly, and if you need to cross-process, you will return a proxy. When cross-process communication, you need to write parameters to the Parcelable object, and then execute the transact function. AIDL generates a proxy class, which will automatically help us write these operations. The difference between intermediary, agent, and appearance mode
The main difference between the two modes: proxy mode and appearance mode is that proxy mode targets a single object, while appearance mode targets all subclasses. Decorative modedefinitionDynamically adds extra responsibilities to an object, and in terms of adding functions, the decorative pattern is more flexible than the method of subclass inheritance. We can usually use inheritance to achieve function expansion. If there are many types of functions that need to be expanded, then many subclasses will inevitably be generated to increase the complexity of the system. At the same time, use inheritance to achieve function expansion, we must foresee these expansion functions. These functions are determined at compile time and are static. The reason for using Decorator is that these functions need to be dynamically determined by the user to join. Decorator provides a "plug-and-play" method to decide when to add what functions during operation. Example public abstract class Component {
public abstract void operate () ;
}
public class ConcreteComponent extends Component {
public void operate () {
//Specific implementation
}
}
public class Decorator extends Component {
private Component component;
public Decorator (Component component) {
this .component = component;
}
public void operate () {
operateA();
component.operate();
operateB();
}
public void operateA () {
//Specific operation
}
public void operateB () {
//Specific operation
}
}
public static void main (String[] args) {
// Use ordinary function classes
Component concreteComponent = new ConcreteComponent();
Component decorator = new Decorator(concreteComponent);
decorator.operate();
}
} If you are careful, you will find that the above call is similar to the call we read when we read the file:
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr); In fact, Java's I/O API is implemented using Decorator. There are many I/O variants. If all inheritance methods are adopted, many subclasses will be produced, which is obviously quite cumbersome. Android: : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : RefactoringIf there are too many subclasses, you can consider using it The difference between decorative mode and proxy mode Decorative mode: extending the function of an object in a transparent way to the client is an alternative to inheritance relationships; Combination modedefinitionThe objects are formed into a tree structure to represent a "part-total" hierarchy, allowing users to have consistency in their use of individual objects and combined objects. Example public class Employee {
private String name;
private String dept;
private int salary;
private List subordinates;
//Constructor
public Employee ( String name,String dept, int sal ) {
this .name = name;
this .dept = dept;
this .salary = sal;
subordinates = new ArrayList();
}
public void add ( Employee e ) {
subordinates.add(e);
}
public void remove ( Employee e ) {
subordinates.remove(e);
}
public List getSubordinates () {
return subordinates;
}
public String toString () {
return ( "Employee :[ Name : " + name
+ ", dept : " + dept + ", salary :"
+ salary+ " ]" );
}
}
public class CompositePatternDemo {
public static void main ( String[] args ) {
Employee CEO = new Employee( "John" , "CEO" , 30000 );
Employee headSales = new Employee( "Robert" , "Head Sales" , 20000 );
Employee headMarketing = new Employee( "Michel" , "Head Marketing" , 20000 );
Employee clerk1 = new Employee( "Laura" , "Marketing" , 10000 );
Employee clerk2 = new Employee( "Bob" , "Marketing" , 10000 );
Employee salesExecutive1 = new Employee( "Richard" , "Sales" , 10000 );
Employee salesExecutive2 = new Employee( "Rob" , "Sales" , 10000 );
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//Print all employees of the organization
System. out .println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System. out .println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System. out .println(employee);
}
}
}
} AndroidThe structure of a View in Android is a tree structure. Each ViewGroup contains a series of Views, and the ViewGroup itself is a View. This is a very typical combination pattern in Android. RefactoringNone Adapter modedefinitionTransform the interface of one class into another that the client expects, so that two classes that were originally unable to work together due to interface mismatch can work together. Example // Target interface, or standard interface, is the interface that customers expect
interface Target {
public void request () ;
}
// Specific target class, only ordinary functions are provided
class ConcreteTarget implements Target {
public void request () {
System.out.println( "Ordinary class has normal functions..." );
}
}
// Classes that already exist, have special functions, but do not meet our existing standard interfaces, are also classes that need to be adapted
class Adaptee {
public void specificRequest () {
System.out.println( "The adapted class has special functions..." );
}
}
// Adapter class, inherits the adapted class, and implements standard interfaces
class Adapter extends Adaptee implements Target {
public void request () {
super .specificRequest();
}
}
// Test class public class Client {
public static void main (String[] args) {
// Use ordinary function classes
Target concreteTarget = new ConcreteTarget();
concreteTarget.request();
// Use special function classes, i.e. adapter classes
Target adapter = new Adapter();
adapter.request();
}
} AndroidTypical are ListView and RecyclerView . Why do ListViews need to use adapters? ListView is used to display list data, but list data has many forms. In order to process and display different data, we need the corresponding adapter as a bridge. In this way, ListView can only care about each ItemView, without caring about what the ItemView is specifically displayed. Our data source stores the content to be displayed, which saves the content to be displayed in each ItemView. There is no relationship between ListView and the data source. At this time, the adapter needs to provide the getView method for the ListView to use. Each time ListView only needs to provide location information to the getView function, and then the getView function obtains the corresponding data from the data source according to the location information, and returns different Views according to the data. RefactoringWhen you want to use an existing class interface, but this existing class is incompatible with the current code structure, you can consider using the adapter mode. Enjoy the Yuan modedefinitionThe English word for Xiangyuan mode is Flyweight, which refers to the lightest weight level in boxing competitions, that is, "flyweight" or "rain level". The translation of "Xianyuan mode" is chosen here because it can better reflect the intention of the mode. Xiangyuan mode is the structure mode of the object. Xiangyuan mode efficiently supports a large number of fine-grained objects in a shared way. Enjoy the Yuan mode is also divided into simple Enjoy the Yuan mode and composite Enjoy the Yuan mode ExampleIn the JAVA language, the String type uses the meta-saving pattern. The String object is a final type, and the object cannot be changed once it is created. In JAVA, string constants are all in the constant pool. JAVA will ensure that a string constant has only one copy in the constant pool. String a=”abc”, where “abc” is a string constant. AndroidWe usually have a lot of contact with the Xiangyuan mode, such as constant pools, thread pools, etc. in Java. It is mainly for reusing objects. Where do you use the Xiangyuan mode on Android? Message in thread communication, calling Message.obtain() every time we get Message is actually to retrieve reusable messages from the message pool to avoid generating a large number of Message objects. RefactoringNone Bridge Modedefinition Separate the abstract parts from the implementation parts so that they can change independently. Take cars driving on the road as an example. There are both cars and buses. They can not only drive on highways in the city, but also on highways. You will find that there are different types of vehicles (cars), and the environment (road) they drive also have different types. In the software system, we must adapt to changes in two aspects (different models and different roads). How can we achieve this change? In software systems, some types have two or more dimensions due to their own logic. How to deal with this "multi-dimensional change"? How to use object-oriented technology to make the type easily change in multiple directions without introducing additional complexity? This requires the use of Bridge mode. Bridge mode is a very useful pattern and very complex. It conforms to the open-closed principle and preferential use of objects rather than inheriting these two object-oriented principles. ExampleFirst, let’s take a look at the above example code of the car when the bridge mode is not applied: //Basic path
class Road {
void run () {
System.out.println( "road" );
}
}
//Downtown street
class Street extends Road {
void run () {
System.out.println( "Downtown Street" );
}
}
//highway
class SpeedWay extends Road {
void run () {
System.out.println( "Highway" );
}
}
//The car is driving in the city streets
class CarOnStreet extends Street {
void run () {
System.out.println( "The car is driving in the city street" );
}
}
//The car is driving on the highway
class CarOnSpeedWay extends SpeedWay {
void run () {
System.out.println( "The car is driving on the highway" );
}
}
//The bus is driving in the city streets
class BusOnStreet extends Street {
void run () {
System.out.println( "Buses travel in downtown streets" );
}
}
//The bus is driving on the highway
class BusOnSpeedWay extends SpeedWay {
void run () {
System.out.println( "Buses travel on highways" );
}
}
//test
public static void main (String[] args) {
//The car is driving on the highway
CarOnSpeedWay carOnSpeedWay = new CarOnSpeedWay();
carOnSpeedWay.run();
//The bus is driving in the city streets
BusOnStreet busOnStreet = new BusOnStreet();
busOnStreet.run();
} However, we say that such a design is fragile. If you analyze it carefully, you will find that it still has many problems. First, while following the principle of open-closing, it violates the single responsibility principle of the class, that is, there is only one reason for its change in a class, and there are two reasons for the change here, namely, the change in the road type and the change in the car type; secondly, there will be many repeated codes, and different cars will also have some codes that are the same when driving on different roads; Again, the structure of the class is too complex, there are too many inheritance relationships, and it is difficult to maintain. The last and most fatal point is that the scalability is too poor. If the change changes along the type of the car and the different roads, we will see that the structure of this class will quickly become huge. abstract class AbstractRoad {
AbstractCar aCar;
void run () {};
}
abstract class AbstractCar {
void run () {};
}
class Street extends AbstractRoad {
@Override
void run () {
// TODO Auto-generated method stub
super .run();
aCar.run();
System.out.println( "Driving in downtown streets" );
}
}
class SpeedWay extends AbstractRoad {
@Override
void run () {
// TODO Auto-generated method stub
super .run();
aCar.run();
System.out.println( "Driving on the highway" );
}
}
class Car extends AbstractCar {
@Override
void run () {
// TODO Auto-generated method stub
super .run();
System.out.print( "Car" );
}
}
class Bus extends AbstractCar {
@Override
void run () {
// TODO Auto-generated method stub
super .run();
System.out.print( "Bus" );
}
}
public static void main (String[] args) {
AbstractRoad speedWay = new SpeedWay();
speedWay.aCar = new Car();
speedWay.run();
AbstractRoad street = new Street();
street.aCar = new Bus();
street.run();
} It can be seen that through object combination, the Bridge pattern changes the inheritance relationship between the two characters into a coupled relationship, so that the two can change independently and calmly, which is also the original intention of the Bridge pattern. AndroidThere are many bridge modes used in Android. For example, for a View, it has two dimensions of changes. One is its description, such as Button, TextView, etc., which are changes in the dimension of the View. The other dimension is to actually draw the View on the screen, which is related to Display, HardwareLayer and Canvas. These two dimensions can be regarded as applications of bridge mode. RefactoringNone InsightsThis article was written based on multiple blog posts, actual projects and personal understanding before starting to read the Gang of Four's "Design Pattern: The Basics of Reusable Object-Oriented Software". It is recommended to look at these two ideas when you have accumulated some accumulation or even encounter some problems that you no longer need to solve with your past experience (this solution refers to the best solution, not just to solve it). Resonance is really important. As for learning the complex technical knowledge of this system, I never recommend reading only a few blog posts. This will only be left on the surface. In addition to refusing to talk about it on paper, many of the most important contents can often be explained in detail in the book, such as how to coordinate various models, what are the differences between each model, and a variety of usage scenarios. How can a few blog posts be learned when one model can write a paper? The first time I watched the design model was two and a half years ago. I hadn't graduated yet. I watched "Da Talk Design Model". At that time, I didn't have much accumulation. The most important thing about the architecture was MVC. So even if I watched "Da Talk", which is famous for its popular "Da Talk", it was still in a fog, and I gave up after reading it for only two chapters. However, after the continuous accumulation of code volume and continuous discovery of problems, I read "Refactoring" and "Design Pattern" again. At this time, I felt completely different. Especially after I did the project reconstruction for a while ago, it is no exaggeration to say that when I read the book "Refactoring", every chapter had the feeling that I felt that I had met too late. The growth in technology is multifaceted. On the one hand, it depends on the accumulation of experience, but the accumulation of experience is limited by many practical factors. On the other hand, the growth is ideological evolution. Of course, this can also be improved by actually doing projects. But similarly, learning through projects will one day reach a bottleneck, which means you should reap the thoughts left by the giants. I have seen theories such as reconstruction and design patterns in some places before. In fact, it is not that reconstruction and design patterns are useless, but that I have not yet reached the level of thinking that they are useful. Design patterns are by no means good topics to talk about definitions occasionally. As one of the three must-read books for Java programmers, "Design Patterns", which can be passed down for such a long time, has its roots. Finally, I would like to complain that the book "Design Pattern" is really a magical book, but it is really obscure and difficult to understand. A large part of the reason is that the four masters of the Gang of Four have a deep understanding of the design pattern, which leads to extremely concise and abstract expressions when writing the book. So after sticking to the first two chapters, he once again gave up and turned to the embrace of "Head First: Design Pattern". The former has more than 200 pages and the latter has more than 700 pages, but everyone knows which one is better to read. However, reconstruction and design patterns themselves cannot be achieved overnight. Both of them need to be continuously applied, re-understood, and re-applied. Only after repeating them can you fully integrate them. Therefore, there is no need to worry about mastering them all after reading them today. Look at them every once in a while, and look back at them when encountering problems. This is the right way to learn these two major ideas. Putting knowledge like "Refactoring" and "Design Patterns" that is more thought-oriented at the beginning of the year is also to further study "Effective Java", "Java Programming Thoughts" and Android source code in the future. The best way to read the source code is not to start from the first line, or to start from the problem, with points and surfaces; or you first try to start from the coder's ideological level. Sometimes when you discover the design ideas and patterns of this module, many things are understood at one point. You even know that you will know the general content of the entire module when you see the class structure of a module, and then just like the code you wrote yourself, it is easy to find anything. Just like if you don't understand generics, even if you have written four or five major projects, you will be confused when you see a bunch of source codes using generic third-party libraries. refer to
|
<<: Tutorial on using TensorFlow on iOS (Part 1)
>>: 0% pride! The WP system also has something worth learning from
[[130368]] Apple today released the fifth beta of...
Recent advances in artificial intelligence are re...
Recently, at the National Breast Cancer Conferenc...
This is an era of information overload. Even very...
From a user research perspective, it is more than...
Recently, epidemics have occurred frequently in v...
“How do I optimize my account?” “How to solve the...
“What is the acceptable click-through rate?” Rega...
There was a very popular test video on the Intern...
The Nuominhe volcanic group in the Oroqen Autonom...
Review expert: Li Weiyang, a well-known popular s...
【Today’s cover】 Golden rays of light pierce throu...
Salt is a necessity for human survival. Throughou...
Produced by: Science Popularization China Produce...
A day or two ago, KFC successfully registered &qu...