-
-
Notifications
You must be signed in to change notification settings - Fork 997
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactoring #352
Refactoring #352
Conversation
Remove getEmoji method and moved to EmojiBSFragment
@lucianocheng @tanoDxyz Can you please review it. |
sure |
Will look this weekend! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All clear
photoeditor/src/main/java/ja/burhanrashid52/photoeditor/PhotoEditor.java
Outdated
Show resolved
Hide resolved
photoeditor/src/main/java/ja/burhanrashid52/photoeditor/PhotoEditor.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two method declarations needs attention
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a look. Overall looks good.
I do think we should stop and consider how the helpers and abstract classes should work re: Graphic
et al. If we want to add functionality in the future, or begin to expose more things to the user, we should be clear where it should go.
* @author <https://github.com/burhanrashid52> | ||
*/ | ||
class GraphicHelper { | ||
private final ViewGroup mViewGroup; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarifying Question: Could you clarify the general purpose of GraphicHelper? By my read, it could be any / all of the below:
- To move operational logic out of
Graphic
abstract class and it's subclasses? - To eventually expose to the user as an interface?
- To deal exclusively with the "helper" box?
I like the idea of having a "Graphic" object, but it looks like here we're using both an Abstract class (class Graphic
) and a Helper class. This is essentially using both inheritance and composition.
In other projects, I tend to see people stick to either inheritance or composition for a set of types (here: Graphic
and it's subclasses). Generally I prefer composition over inheritance (see: Effective Java, Item 16), or inheritance from an interface, but I could see the argument here for either.
Would like more information to render a opinion for the review. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My initial thought was the same. I extracted GraphicHelper
exclusively to deal with the helper box. But then I thought of giving a generic name that might be used in the future to handle more cases related to these boxes. I understand your point in mixing two concepts In graphics.
Maybe I can move the GraphicHelper
outside the Graphics
and rename it as BoxHelper
. Would it make sense?
I was having two thoughts on whether to keep Graphic
as an abstract class or interface. That's why I needed a third-person opinion. I personally like to have a Graphic interface. But we have a repetitive view creation and listener logic that can be converted into Template Pattern using abstract class.
I tried first with the interface and was not happy with the result and hence abstract class made sense to me. Just thinking out loud maybe we can use both abstract and interface Example: Sticker extends Graphics implements IGraphic
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I can move the GraphicHelper outside the Graphics and rename it as BoxHelper. Would it make sense?
Thank you for doing this. BoxHelper
is a better name IMO.
I was having two thoughts on whether to keep Graphic as an abstract class or interface. That's why I needed a third-person opinion. I personally like to have a Graphic interface. But we have a repetitive view creation and listener logic that can be converted into Template Pattern using abstract class.
Yep. Speaking strictly for myself, I've found using Abstract classes and a Template Pattern can degrade if the complexity grows. Specifically, if there is a lot of needed functionality, or a very deep tree of types, the abstract classes get cluttered with tons of functionality. Due to the nature of inheritance, the abstract class functionality gets included by default in every subclass. At some point the subclasses diverge on what they expect the superclass / abstract class functions to do, so there's proliferation. In the end it can result in a lot of tangling.
Note this isn't the case if the abstract class is an interface to a 3rd party, where the definitions are fixed and compliance with the template "contract" helps keep the user / library interface clean.
In this case, it's not complex enough to merit more work. Since this is strictly internal, we can switch the approach if it gets out of hand.
I tried first with the interface and was not happy with the result and hence abstract class made sense to me. Just thinking out loud maybe we can use both abstract and interface Example: Sticker extends Graphics implements IGraphic Thoughts?
Since none of this is exposed to the user, I'm fine leaving it either way. If it becomes unwieldy down the line we can switch it up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah forgot to mention one thing: Generally speaking, Template Pattern / Abstract classes are harder to test, since inheritance trees cannot be replaced with mock objects.
Doesn't change my answer from above, just wanted to flag it.
}); | ||
} | ||
|
||
public interface PhotoEditor { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Is the purpose of turning this into an Interface to use a builder pattern, and expose limited functions to the end-user? Or is it to eventually implement different types of PhotoEditor objects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intention is to expose limited functionality to the user and to easily mock/fake out the PhotoEditor
in the test if needed
* | ||
* @author <https://github.com/burhanrashid52> | ||
*/ | ||
class PhotoSaverTask extends AsyncTask<String, String, PhotoSaverTask.SaveResult> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: Probably out of scope, but AsyncTask has been deprecated. On my local branch I switched to using ThreadPools.
https://blog.mindorks.com/threadpoolexecutor-in-android-8e9d22330ee3
Also stops the (highly unlikely) edge case of someone running this twice (in a threadpool, they would be queued).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct. This needs to be changed. The intention was here more of refactoring without changing much of internal logic. We plan this after this release.
return new SaveResult(null, mImagePath, null); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
Log.d(TAG, "Failed to save File"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I would log this as an error (Log.e(...)
)
out.close(); | ||
Log.d(TAG, "Filed Saved Successfully"); | ||
return new SaveResult(null, mImagePath, null); | ||
} catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I would narrow this catch
block, either by catching IOException
or creating a union of exception types using the |
operator. Saves you from catching and squashing things like NullPointerException
.
/** | ||
* Created by Burhanuddin Rashid on 18/05/21. | ||
* | ||
* @author <https://github.com/burhanrashid52> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we have any tests that execute the saving logic, but this might be a good opportunity to add one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I research in the past how to achieve this. But could not find a solution for it. The closest thing I found was Facebook Screenshot testing and this Airbnb article.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 . Yea might require a lot of mocking to get this to work.
* | ||
* @author <https://github.com/burhanrashid52> | ||
*/ | ||
abstract class Graphic { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Complement: This is a good name for this class.
Question: Is this meant to eventually contain the brush logic as well, or just emoji/text/sticker?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The brush view is created only once and stays on top of the Graphic stack. That cause a few issues #268 . So for now it's for emoji/text/sticker.
|
||
@Override | ||
ViewType getViewType() { | ||
return ViewType.IMAGE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potentially dumb question: Any reason we're not changing this to be ViewType.STICKER
? Out of scope? Maybe this is exposed to the user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, It's exposed to the user in OnPhotoEditorListener
and hence breaks the client code.
private final GraphicManager mGraphicManager; | ||
private final ViewGroup mPhotoEditorView; | ||
private final PhotoEditorViewState mViewState; | ||
private TextView txtText; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: Maybe textGraphicView
or graphicTextView
as a identifier? or just textView
?
@Override | ||
void setupView(View rootView) { | ||
txtText = rootView.findViewById(R.id.tvPhotoEditorText); | ||
if (txtText != null && mDefaultTextTypeface != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Complement: Glad we're adding these null checks. Gives us flexibility in the future to change the XML templates without worrying about breaking the code.
First of all, thanks for the review. I've replied to comments. You can resolve the conversation as per the answer. Happy to continue the discussion on other points. |
No description provided.