많은 정적 메서드를 사용하는 것이 나쁜가요?
클래스가 내부 상태를 추적 할 필요가 없을 때 클래스의 모든 메서드를 정적으로 선언하는 경향이 있습니다. 예를 들어 A를 B로 변환해야하고 변할 수있는 내부 상태 C에 의존하지 않는 경우 정적 변환을 만듭니다. 조정할 수있는 내부 상태 C가있는 경우 생성자를 추가하여 C를 설정하고 정적 변환을 사용하지 않습니다.
정적 메서드를 과도하게 사용하지 말라는 다양한 권장 사항 (StackOverflow 포함)을 읽었지만 위의 경험 법칙에서 무엇이 잘못되었는지 이해하지 못했습니다.
그게 합리적인 접근입니까?
일반적인 정적 메서드에는 두 가지 종류가 있습니다.
- "안전한"정적 메서드는 항상 동일한 입력에 대해 동일한 출력을 제공합니다. 전역을 수정하지 않으며 클래스의 "안전하지 않은"정적 메서드를 호출하지 않습니다. 기본적으로 제한된 종류의 함수형 프로그래밍을 사용하고 있습니다. 두려워하지 마세요. 괜찮습니다.
- "안전하지 않은"정적 메서드는 전역 상태 나 프록시를 전역 개체 또는 기타 테스트 할 수없는 동작으로 변경합니다. 이는 절차 적 프로그래밍에 대한 후퇴이며 가능하면 리팩토링해야합니다.
예를 들어, Singleton 패턴에서 "안전하지 않은"정적의 몇 가지 일반적인 사용이 있지만 이름이 예쁜 이름에도 불구하고 전역 변수를 변경하는 것입니다. 안전하지 않은 정적을 사용하기 전에 신중하게 생각하십시오.
내부 상태가없는 개체는 의심스러운 것입니다.
일반적으로 객체는 상태와 동작을 캡슐화합니다. 행동만을 캡슐화하는 객체는 이상합니다. 때로는 Lightweight 또는 Flyweight 의 예입니다 .
다른 경우에는 객체 언어로 수행되는 절차 설계입니다.
이것은 John Millikin의 훌륭한 답변에 대한 후속 조치 일뿐입니다.
상태 비 저장 메서드 (대부분의 함수)를 정적으로 만드는 것이 안전 할 수 있지만 때로는 수정하기 어려운 결합으로 이어질 수 있습니다. 다음과 같은 정적 메서드가 있다고 가정합니다.
public class StaticClassVersionOne {
public static void doSomeFunkyThing(int arg);
}
당신이 부르는 :
StaticClassVersionOne.doSomeFunkyThing(42);
정적 메서드의 동작을 수정해야하는 경우를 발견 할 때까지 모두 훌륭하고 매우 편리하며 StaticClassVersionOne. 아마도 코드를 수정할 수 있고 괜찮을 것입니다. 그러나 이전 동작에 의존하는 다른 호출자가 있다면 메서드 본문에 설명해야합니다. 어떤 경우에는 메서드 본문이 이러한 모든 동작의 균형을 맞추려고하면 매우 추악하거나 유지 관리 할 수 없게 될 수 있습니다. 메서드를 분할하는 경우이를 고려하기 위해 여러 위치에서 코드를 수정하거나 새 클래스를 호출해야 할 수 있습니다.
그러나 메소드를 제공하기 위해 인터페이스를 생성하고 호출자에게 제공했다면 이제 동작이 변경되어야 할 때 인터페이스를 구현하기 위해 새 클래스를 생성 할 수 있습니다.이 인터페이스는 더 깔끔하고, 더 쉽게 테스트되며, 유지 관리가 더 쉽습니다. 대신 호출자에게 제공됩니다. 이 시나리오에서는 호출 클래스를 변경하거나 다시 컴파일 할 필요가 없으며 변경 사항이 지역화됩니다.
가능성이있는 상황 일 수도 있고 아닐 수도 있지만 고려할 가치가 있다고 생각합니다.
다른 옵션은 원본 객체에 비 정적 메서드로 추가하는 것입니다.
즉, 변경 :
public class BarUtil {
public static Foo transform(Bar toFoo) { ... }
}
으로
public class Bar {
...
public Foo transform() { ...}
}
however in many situations this isn't possible (e.g., regular class code generation from XSD/WSDL/etc), or it will make the class very long, and transformation methods can often be a real pain for complex objects and you just want them in their own separate class. So yeah, I have static methods in utility classes.
The reason you are warned away from static methods is that using them forfeits one of the advantages of objects. Objects are intended for data encapsulation. This prevents unexpected side effects from happening which avoids bugs. Static methods have no encapsulated data* and so don't garner this benefit.
That said, if you have no use of internal data, they are fine to use and slightly faster to execute. Make sure you aren't touching global data in them though.
- Some languages also have class-level variables which would allow for encapsulation of data and static methods.
Static classes are fine as long as they're used in the right places.
Namely: Methods that are 'leaf' methods (they do not modify state, they merely transform the input somehow). Good examples of this are things like Path.Combine. These sorts of things are useful and make for terser syntax.
The problems I have with statics are numerous:
Firstly, if you have static classes, dependencies are hidden. Consider the following:
public static class ResourceLoader
{
public static void Init(string _rootPath) { ... etc. }
public static void GetResource(string _resourceName) { ... etc. }
public static void Quit() { ... etc. }
}
public static class TextureManager
{
private static Dictionary<string, Texture> m_textures;
public static Init(IEnumerable<GraphicsFormat> _formats)
{
m_textures = new Dictionary<string, Texture>();
foreach(var graphicsFormat in _formats)
{
// do something to create loading classes for all
// supported formats or some other contrived example!
}
}
public static Texture GetTexture(string _path)
{
if(m_textures.ContainsKey(_path))
return m_textures[_path];
// How do we know that ResourceLoader is valid at this point?
var texture = ResourceLoader.LoadResource(_path);
m_textures.Add(_path, texture);
return texture;
}
public static Quit() { ... cleanup code }
}
Looking at TextureManager, you cannot tell what initialisation steps must be carried out by looking at a constructor. You must delve into the class to find its dependencies and initialise things in the correct order. In this case, it needs the ResourceLoader to be initialised before running. Now scale up this dependency nightmare and you can probably guess what will happen. Imagine trying to maintain code where there is no explicit order of initialisation. Contrast this with dependency injection with instances -- in that case the code won't even compile if the dependencies are not fulfilled!
Furthermore, if you use statics that modify state, it's like a house of cards. You never know who has access to what, and the design tends to resemble a spaghetti monster.
Finally, and just as importantly, using statics ties a program to a specific implementation. Static code is the antithesis of designing for testability. Testing code that is riddled with statics is a nightmare. A static call can never be swapped for a test double (unless you use testing frameworks specifically designed to mock out static types), so a static system causes everything that uses it to be an instant integration test.
In short, statics are fine for some things and for small tools or throwaway code I wouldn't discourage their use. However, beyond that, they are a bloody nightmare for maintainability, good design and ease of testing.
Here's a good article on the problems: http://gamearchitect.net/2008/09/13/an-anatomy-of-despair-managers-and-contexts/
That seems to be a reasonable approach. The reason you don't want to use too many static classes/methods is that you end up moving away from object oriented programming and more into the realm of structured programming.
In your case where you are simply transforming A to B, say all we're doing is transforming text to go from
"hello" =>(transform)=> "<b>Hello!</b>"
Then a static method would make sense.
However, if you're invoking these static methods on an object frequently and it tends to be unique for many calls (e.g. the way you use it depends on the input), or it is part of the inherent behavior of the object, it would be wise to make it part of the object and maintaining a state of it. One way to do this would be to implement it as an interface.
class Interface{
method toHtml(){
return transformed string (e.g. "<b>Hello!</b>")
}
method toConsole(){
return transformed string (e.g. "printf Hello!")
}
}
class Object implements Interface {
mystring = "hello"
//the implementations of the interface would yield the necessary
//functionality, and it is reusable across the board since it
//is an interface so... you can make it specific to the object
method toHtml()
method toConsole()
}
Edit: One good example of great use of static methods are html helper methods in Asp.Net MVC or Ruby. They create html elements that aren't tied to the behavior of an object, and are therefore static.
Edit 2: Changed functional programming to structured programming (for some reason I got confused), props to Torsten for pointing that out.
I recently refactored an application to remove/modify some classes that had been initially implemented as static classes. Over time these classes acquired so much and people just kept tagging the new functions as static, since there was never an instance floating around.
So, my answer is that static classes aren't inherently bad but it might be easier to start creating instances now, then have to refactor later.
I used to go back and forth between a class with a bunch of static methods and a singleton. Both solve the problem, but the singleton can be much more easily replaced with more than one. (Programmers always seem so certain that there will only be 1 of something and I found myself wrong enough times to completely give up on static methods except in some very limited cases).
Anyway, the singleton gives you the ability to later pass something into the factory to get a different instance and that changes the behavior of your entire program without refactoring. Changing a global class of static methods into something with different "backing" data or a slightly different behavior (child class) is a major pain in the butt.
And static methods have no similar advantage.
So yes, they are bad.
I'd consider it a design smell. If you find yourself using mostly static methods, you probably don't have a very good OO design. It's not necessarily bad, but as with all smells it would make me stop and re-evaluate. It hints that you might be able make a better OO design, or that maybe you should go the other direction and avoid OO entirely for this problem.
As long as not internal state comes into play, this is fine. Note that usually static methods are expected to be thread-safe, so if you use helper data structures, use them in a thread-safe manner.
If you know you will never need to use the internal state of C, it's fine. Should that ever change in the future, though, you'd need to make the method non-static. If it's non-static to begin with, you can just ignore the internal state if you don't need it.
If it's a utility method, it's nice to make it static. Guava and Apache Commons are built upon this principle.
My opinion on this is purely pragmatic. If it's your app code, static methods are generally not the best thing to have. Static methods have serious unit-testing limitations - they can't be easily mocked: you can't inject a mocked static functionality into some other test. You also can't usually inject functionality into a static method.
So in my app logic I usually have small static utility-like method calls. I.e.
static cutNotNull(String s, int length){
return s == null ? null : s.substring(0, length);
}
one of the benefits is that I don't test such methods :-)
Well, there is no silver bullet of course. Static classes are ok for little utilities/helpers. But using static methods for business logic programming is certainly evil. Consider the following code
public class BusinessService
{
public Guid CreateItem(Item newItem, Guid userID, Guid ownerID)
{
var newItemId = itemsRepository.Create(createItem, userID, ownerID);
**var searchItem = ItemsProcessor.SplitItem(newItem);**
searchRepository.Add(searchItem);
return newItemId;
}
}
You see a static method call to ItemsProcessor.SplitItem(newItem); It smells cause
- You dont have explicit dependency declared and if you don't dig into code you might overlook the coupling between your class and static method container
- You can't test
BusinessServiceisolating it fromItemsProcessor(most test tools do not mock static classes) and it makes unit testing impossible. No Unit Tests == low quality
Static methods are generally a bad choice even for stateless code. Instead make a singleton class with these methods that is instantiated once and injected into those classes wanting to use the methods. Such classes are easier to mock and test. They are far more object oriented. You can wrap them with a proxy when needed. Statics make OO much harder and I see no reason to use them in almost all cases. Not 100% but almost all.
참고URL : https://stackoverflow.com/questions/752758/is-using-a-lot-of-static-methods-a-bad-thing
'developer tip' 카테고리의 다른 글
| Subversion에 커밋 한 후 "프로젝트의 종속성을 업데이트 할 수 없습니다" (0) | 2020.09.07 |
|---|---|
| AWS SNS를 사용한 FCM (0) | 2020.09.07 |
| 엔터티 프레임 워크 코드에서 먼저 여러 열에 KeyAttribute를 사용하는 방법 (0) | 2020.09.07 |
| 두 개체를 비교하고 차이점 찾기 (0) | 2020.09.07 |
| new Date ()는 Chrome과 Firefox에서 다르게 작동합니다. (0) | 2020.09.06 |